体系结构

64.综述

64.1. NoSQL?

Hbase是一种“NoSQL”数据库。“NoSQL”是一个通用术语,代表该数据库不支持SQL数据库作为其主要访问语言。有许多种NoSQL数据库: BerkeleyDB是本地NoSQL数据库的例子,而HBase是分布式数据库。技术上讲,HBase比起数据库,更像一个“数据存储”。因为它缺少很多RDBMS中的特性,如类型列,第二索引,触发器和公安机关查询语言等。

当然,Hbase具有许多特性支持线性和模块化。通过增加托管在有用类服务器上的region server来扩展Hbase集群。例如,如果一个集群从10个RegionServer扩展到20个,它的处理能力和存储容量都翻倍。An RDBMS can scale well, but only up to a point-特别是,单个数据库服务器的规模-最好的的性能要求特殊的硬件和存储设备。Hbase的特性有:

  • 强读写一致性:Hbase不是一个最终一致的数据存储。这使它非常适用于高速计数器聚合类的任务。
  • 自动分片:HBase表通过区域分布在集群上,随着数据增多,区域会自动分割并重新分布。
  • regionserver自动故障转移。
  • Hadoop/HDFS整合:HBase天生支持HDFS作为其分布式文件系统。
  • MapReduce: 通过使用HBase作为源和汇的MR,Hbase支持大量并行处理。
  • java client api:HBase支持一个易于使用的java API的编程访问。
  • Thrift/REST API:HBase也支持Thrift 和Rest非java前端。
  • Block Cache and Bloom Filters:Hbase支持块缓存和Bloom fliters以实现大批量查询优化。
  • Operational Management运营管理: HBase提供建于网页的操作视角以及JMX标准。

64.2.何时应该使用HBase?

HBase不适合所有问题。

首先,确保你有足够数据,如果你有许多许多行,Hbase是个不错的选择。如果你只有几千行,或许使用传统RDBMS是个更换的选择,因为你所有的数据可能围绕一个单一(或2个)节点而余下的集群则闲置下来。

第二,确保你不需要RDBMS提供的那些额外功能(如类型列,次要索引,transaction,高级查询语言),一个建立在RDBMS上的应用程序不能通过简单地改变JDBC驱动程序就移植到HBase上。例如,要考虑好从RDBMS移植到HBase是一个完整的重新设计,而不是一个端口。

第三, 确保你有足够多的硬件,即使是HDFS在没有多于5个DataNodes的情况下也无法正常工作,因为默认HDfS块有三个副本,加一个NameNode。

HBase可以在笔记本上很好的运行stand-alone模式,但这只是一个开发配置而已。

64.3.HBase与Hadoop/HDFS之间的区别

HDFS是分布式文件系统很适合存储大文件。它的文档声称,无论怎样,它不是一个通用的文件系统,不支持快速个人数据的文件查询。换句话说,HBase是建立在HDFS之上,并提供巨大表格的快速记录查询。这有时会让人产生概念上的困扰。Hbase内部将数据存储在被索引的StoreFiles中,这个文件存在于HDFS中可供高速查询。

65. 目录表

目录表hbase:meta是HBase中的一张表,当使用list命令时可以显示器内容,但和其他表一样就是一张表。

65.1. -ROOT-

-ROOT-表在Hbase0.96.0中被移除了。

0.96之前-ROOT-表记录了.META表(之前的叫法,现在叫hbae:meta)的位置。-ROOT-结构如下:

Key

  • .META. region key (.META.,,1)

Values

  • info:regioninfo (hbase:meta的序列化HRegionInfo实例)
  • info:server(server:持有hbase:meta的RegionServer端口)
  • info:serverstartcode(持有hbase:meta的RegionServer进程的开始时间)

65.2.hbase:meta

hbase:meta表之前称为.META在系统中记录包含所有区域的一张表。hbase:meta的位置之前可以在-ROOT-表中获取,如今存储在ZooKeeper中。

其结构如下:

Key

  • 格式的区域键([table],[region start key],[region id])

Values

  • info:regioninfo (这个区域的序列化HRegionInfo实例)
  • info:server(server:包含这个区域的RegionServer端口)
  • info:serverstartcode(包含这个区域的RegionServer进程开始时间)

当一个表正处于分割进程时,两列将被创建,叫做info:splitA和info:splitB。这两列代表两个子区域。这两列的值也是序列化的HRegionInfo实例。在区域分裂完毕之后,这行将被删除。

65.3. 开始序列

首先,在Zookeeper中查到hbase:meta位置,然后,使用server和startcode更新hbase:meta。

66.客户端

客户端找出为特定感兴趣范围行服务的RegionSevers.通过查询hbase:meta表。hbase:meta for更多细节。在定位好要求的区域后,客户端与服务那个region的RegionServer进行联系,而不是主节点,然后发送读写请求。这个信息被缓存在客户端,以便后续请求不需要进行查找流程。区域应该被重新分配或者通过主节点负载均衡或因为某个RegionServer挂掉,客户端将查询目录表来确定用户区的新位置。

See Runtime Impact for more information about the impact of the Master on HBase Client communication.

Administrative functions are done via an instance of Admin

66.1.集群连接

The API changed in HBase 1.0. For connection configuration information, see Client configuration and dependencies connecting to an HBase cluster.

66.1.1HBase 1.0.0的API

It’s been cleaned up and users are returned Interfaces to work against rather than particular types.在HBase1.0中,从ConnectionFactory获得一个连接对象,然后按需从中获得它的实例表、管理员和RegionLocator。当完成后,关闭获取的实例。最终,确保在退出前清理你的Connection实例。连接时重量级对象,但线程安全,所以可以为应用创建一个,并保持这个实例。Table,Admin以及RegionLocator实例是轻量级的。使用时创建,用完后尽快释放。

66.1.2. HBase1.0.0之前的API

HTable实例是1.0版之前与HBase集群交互的方式。Table实例不是线程安全的。在任何给定时间,表实例只能被一个线程使用。当创建表实例时,建议使用同样的HBaseConfiguration实例。这样可以保证共享RegionServers的ZooKeeper和socket实例,这通常是你想要的。例如,推荐如下:

  1. HBaseConfiguration conf = HBaseConfiguration.create();
  2. HTable table1 = new HTable(conf, "myTable");
  3. HTable table2 = new HTable(conf, "myTable");

反对这样:

  1. HBaseConfiguration conf1 = HBaseConfiguration.create();
  2. HTable table1 = new HTable(conf1, "myTable");
  3. HBaseConfiguration conf2 = HBaseConfiguration.create();
  4. HTable table2 = new HTable(conf2, "myTable");

For more information about how connections are handled in the HBase client, see ConnectionFactory.

连接池 对于需要high-end多线程访问的应用程序(web-servers或application-servers在一个JVM中提供多个应用程序线程),可以先创建一个连接,像下面例子这样。

Example 37. Pre-Creating a Connection

  1. // Create a connection to the cluster.
  2. Configuration conf = HBaseConfiguration.create();
  3. try (Connection connection = ConnectionFactory.createConnection(conf)) {
  4. try (Table table = connection.getTable(TableName.valueOf(tablename)) {
  5. // use table as needed, the table returned is lightweight
  6. }
  7. }

构建HTableInterfaces实现是非常轻量级,并且资源可控的。

66.2. WriteBuffer and Batch Methods

在HBase1.0之后,HTable被反对而支持Table.Table不使用autoflush。要做缓冲写入,使用BufferedMutator类。

在Table或HTable被丢弃之前,调用close()或flushCommits(), 这样Put不会丢失。

For additional information on write durability, review the ACID semantics page.

For fine-grained control of batching of Puts or Deletes, see the batch methods on Table.

66.3 外部ClientExternal客户端

Information on non-Java clients and custom protocols is covered in Apache HBase External APIs。

67. 客户请求过滤器

Get和Scan实例可以可选地配置应用在RegionServer上的过滤器。

过滤器可能很烦人因为会有许多不同种类,理解组的过滤功能是最好的了解他们的方法。

67.1. Structural

Structural过滤包含其他过滤器。

67.1.1.过滤表

过滤表代表过滤表之间FilterList.Operator.MUST_PASS_ALL或FilterList.Operator.MUST_PASS_ONE关系的过滤表。下面是or过滤的例子

  1. FilterList list = new FilterList(FilterList.Operator.MUST_PASS_ONE);
  2. SingleColumnValueFilter filter1 = new SingleColumnValueFilter(
  3. cf,
  4. column,
  5. CompareOp.EQUAL,
  6. Bytes.toBytes("my value")
  7. );
  8. list.add(filter1);
  9. SingleColumnValueFilter filter2 = new SingleColumnValueFilter(
  10. cf,
  11. column,
  12. CompareOp.EQUAL,
  13. Bytes.toBytes("my other value")
  14. );
  15. list.add(filter2);
  16. scan.setFilter(list);

67.2 列值

67.2.1.单列值过滤

SingleColumnValueFilter可以被用来测试列值相等(CompareOp.EQUAL)、不相等(CompareOp.NOT_EQUAL))或范围(e.g., CompareOp.GREATER)。下面是一个测试列值等于“my value”的例子。

  1. SingleColumnValueFilter filter = new SingleColumnValueFilter(
  2. cf,
  3. column,
  4. CompareOp.EQUAL,
  5. Bytes.toBytes("my value")
  6. );
  7. scan.setFilter(filter);

67.3.列值比较

有几个比较类的过滤包,是值得介绍的。这些过滤器与其他过滤器一起使用,如上面介绍的 SingleColumnValueFilter.

67.3.1.正则表达式Comparator

RegexStringComparator支持值比较的常规表达。

  1. RegexStringComparator comp = new RegexStringComparator("my."); // any value that starts with 'my'
  2. SingleColumnValueFilter filter = new SingleColumnValueFilter(
  3. cf,
  4. column,
  5. CompareOp.EQUAL,
  6. comp
  7. );
  8. scan.setFilter(filter);

67.3.2. 子串比较器

子串比较器用于决定一个给定的子串是否存于一个值中。这种比较是大小写敏感的。

  1. SubstringComparator comp = new SubstringComparator("y val"); // looking for 'my value'
  2. SingleColumnValueFilter filter = new SingleColumnValueFilter(
  3. cf,
  4. column,
  5. CompareOp.EQUAL,
  6. comp
  7. );
  8. scan.setFilter(filter);

67.3.3.BinaryPrefixComparator

67.3.4. BinaryComparator

67.4. 键值元数据

HBase内部以键值对的方式存储数据, KeyValue Metadata Filters evaluate the existence of keys (i.e., ColumnFamily:Column qualifiers) for a row, as opposed to values the previous section.

67.4.1. familyFilter

FamilyFilter 可以用在过滤 ColumnFamily上.Scan中选择ColumnFamliy比用过滤,通常是一个更好的主意。

67.4.2. QualifierFilter

QualifierFilter可用来列名基础上的过滤。

67.4.3. ColumnPrefixFilter

ColumnPrefixFilter可用于基于部分列名的过滤。

ColumnPrefixFilter寻找每个相关列族和每列中第一个匹配的前缀。可以用来在很宽的行中高效的获取列的子集。

注意:相同的列限定符可在不同的列族中使用。这个过滤器返回所有匹配的列。

例子:在行和族中找到以”abc”开头的列

  1. HTableInterface t = ...;
  2. byte[] row = ...;
  3. byte[] family = ...;
  4. byte[] prefix = Bytes.toBytes("abc");
  5. Scan scan = new Scan(row, row); // (optional) limit to one row
  6. scan.addFamily(family); // (optional) limit to one family
  7. Filter f = new ColumnPrefixFilter(prefix);
  8. scan.setFilter(f);
  9. scan.setBatch(10); // set this if there could be many columns returned
  10. ResultScanner rs = t.getScanner(scan);
  11. for (Result r = rs.next(); r != null; r = rs.next()) {
  12. for (KeyValue kv : r.raw()) {
  13. // each kv represents a column
  14. }
  15. }
  16. rs.close();

67.4.4. MultipleColumnPrefixFilter

MultipleColumnPrefixFilter和列前缀过滤类似,只不过允许指定多个前缀。Like ColumnPrefixFilter, MultipleColumnPrefixFilter 有效地从列开头查找匹配的前缀中 and also seeks past ranges of columns between prefixes.它可以有效地从广泛的行获得不连续的列。

Example: Find all columns in a row and family that start with “abc” or “xyz”

  1. HTableInterface t = ...;
  2. byte[] row = ...;
  3. byte[] family = ...;
  4. byte[][] prefixes = new byte[][] {Bytes.toBytes("abc"), Bytes.toBytes("xyz")};
  5. Scan scan = new Scan(row, row); // (optional) limit to one row
  6. scan.addFamily(family); // (optional) limit to one family
  7. Filter f = new MultipleColumnPrefixFilter(prefixes);
  8. scan.setFilter(f);
  9. scan.setBatch(10); // set this if there could be many columns returned
  10. ResultScanner rs = t.getScanner(scan);
  11. for (Result r = rs.next(); r != null; r = rs.next()) {
  12. for (KeyValue kv : r.raw()) {
  13. // each kv represents a column
  14. }
  15. }
  16. rs.close();

67.4.5列范围过滤器

列范围过滤器允许有效地行内扫描。

ColumnRangeFilter 从相关列族的开头开始查找第一个匹配。可以很高效地从很宽的一列中获得一列的一部分。如,在一行中你有一百万列,但你只想查看列bbbb-bbbd。

注意:在不同列族中可能使用相同列名。这个过滤器返回所有匹配的列。

Example: Find all columns in a row and family between “bbbb” (inclusive) and “bbdd” (inclusive)

  1. HTableInterface t = ...;
  2. byte[] row = ...;
  3. byte[] family = ...;
  4. byte[] startColumn = Bytes.toBytes("bbbb");
  5. byte[] endColumn = Bytes.toBytes("bbdd");
  6. Scan scan = new Scan(row, row); // (optional) limit to one row
  7. scan.addFamily(family); // (optional) limit to one family
  8. Filter f = new ColumnRangeFilter(startColumn, true, endColumn, true);
  9. scan.setFilter(f);
  10. scan.setBatch(10); // set this if there could be many columns returned
  11. ResultScanner rs = t.getScanner(scan);
  12. for (Result r = rs.next(); r != null; r = rs.next()) {
  13. for (KeyValue kv : r.raw()) {
  14. // each kv represents a column
  15. }
  16. }
  17. rs.close();

67.5.行键

67.5.1.行过滤器

为了选择行,在Scan中使用startRow/stopRow方法是个好主意,当然也可以使用RowFilter。

67.6 Utility

67.6.1 FirstKeyOnlyFilter

This is primarily used for rowcount jobs.

68. Master

hmaster是主服务器的实现。Master服务器负责检测集群中所有的RegionServer实例, 是所有元数据改变的接口。在分布式集群中,Master一般在NameNode上运行。

68.1.启动行为

如果在多Master环境中运行,所有Master竞争运行集群。如果主Master失去租约(或Master关闭),那么剩下的Master争相成为active Master.

68.2.运行时影响

一个常见的分布式问题涉及到当Master挂掉,Hbase集群将发生什么。因为HBase客户端直接和RegionServer通信,集群将仍然处于“steady state”的功能状态。另外,每个目录表,hbase:meta作为Hbase 表存在,而不是待在Master中。当然,Master控制一些重要的功能,如regionserver的故障转移和完成区域分割。所以,集群仍可以在没有Master情况下运行一段时间,Master应尽可能快得重启。

68.3.接口

HMasterinterface开放的方法主要是面向元数据的方法:

  • Table(createTable, modifyTable,removeTalbe,enable, disable)
  • ColumnFamily(addColumn, modifyColumn,removeColumn)
  • Region(move, assign, unassign) 例如当引用Admin方法disableTable时,它是由Master服务器服务的。

68.4.进程

Master提供一些背景线程:

68.4.1.负载均衡

周期性地,当没有区域在转换时,负载均衡器将运行起来,将区域移动来平衡集群负载。 See Balancer for configuring this property.

See Region-RegionServer Assignment for more information on region assignment.

68.4.2. CatalogJanitor

周期性地检查并清理hbase:meta表。

69. 区域服务器

HRegionServer是RegionServer的实现。它负责服务和管理区域。在分布式集群中,区域服务器运行在DataNode上。

69.1.接口

HRegionRegionInterface开放的方法包括面向数据的和区域维护的方法:

  • Data(get, put, delete, next, etc.)
  • Region(splitRegion, compactRegion, 等)当Admin的majorCompact方法对某个表被调用时,客户端是遍历所有区域查找指定的表,并直接向每个区域请求主紧缩。

69.2.进程

区域服务器运行多种多样的背景线程:

69.2.1 主分割线程

Checks for splits and handle minor compactions.

69.2.2. MajorCompactionChecker

Checks for major compactions.

69.2.3. MemStoreFlusher

周期性刷新内存将MemStore中内容写入StoreFiles.

69.2.4. LogRoller

周期性检查RegionServer的WAL

69.3.协处理器

协处理器是在0.92.中别加入的。There is a thorough Blog Overview of CoProcessors posted. Documentation will eventually move to this reference guide, but the blog is the most current information available at this time.

69.4.块缓存

HBase提供两种不同的块缓存实现:默认的堆上 LruBlockCache和BucketCache(非堆内存)。这小节将讨论每种方法的优缺点,怎样选择合适的方法,以及每种选择的配置方法。

69.4.1.缓存选择

LruBlockCache是最初的方法,全部在Java堆中。BucketCache主要用来在非堆内存中保存块缓存,尽管BucketCache也能在堆内存上保存数据并从文件支持的缓存中服务。

当从BucketCache中取永远比本地堆上的LruBlockCache慢。当然,延迟往往是不稳定的时间, 因为当使用BucketCache时,会有更少的垃圾收集,因它是管理blockcache分配,不是GC。如果BucketCache使用非堆内存模式,这种内存完全不适用GC管理。这是为什么你使用BucketCache, 为了减轻GCS和堆碎片延迟不稳定,See Nick Dimiduk’s BlockCache 101 for comparisons running on-heap vs off-heap tests. Also see Comparing BlockCache Deploys which finds that if your dataset fits inside your LruBlockCache deploy, use it otherwise if you are experiencing cache churn (or you want your cache to exist beyond the vagaries of java GC), use BucketCache.

当启用BucketCache,你使用一个二级缓存系统,第一层缓存由LruBlockCache实例实现,第二层的非堆内存是由BucketCache实现。这两层的管理和政策决定块之间移动是怎样通过CombinedBlockCache实现的。L2 BucketCache中保存了所有DATA块,而元数据-INDEX和BLOOM块在L1 LruBlockCache的堆内存中。

69.4.2.一般缓存设置

除了缓存实现本身之外,您也可以设置一些一般性的配置选项来控制缓存如何执行。在设置任何这些选项后,重新启动或轮流重新启动您的集群使配置生效。发生错误和未知行为请查看日志。

69.4.3 LruBlockCache Design

LruBlockCache是一个LRU缓存,包含3个层面的块优先级,允许列族内存性能和内存扫描:

  • 单一接入优先级:第一次块被从HDFS中加载出来,它通常具有这类优先级,属于evitions阶段要考虑的第一组。优点是扫描块比使用越来越多的使用块更容易被evicted。
  • 多接入优先级:如果前面优先级组的块被再次访问,则将升级到整个优先级。因此,它是evictions时第二组要被考虑的。
  • 内存接入优先级:如果某个块的族被配置为“内存”,它将成为这类优先级不管本次访问的次数。目录表被设置为此类。这是evictions时最后要考虑的。

要将某个列族标记为in-memory, 调用

  1. HColumnDescriptor.setInMemory(true);

如果从java创建某个表,或当在shell中创建或更改表时设置IN_MEMORY=>true : 如

  1. hbase(main):003:0> create 't', {NAME => 'f', IN_MEMORY => 'true'}

69.4.4 LruBlockCache 使用

块缓存对用户表时默认打开的,这意味着任何读操作将加载LRU缓存。这可能对大部分用例都是好的,但进一步调优通常是需要的为了获得更好的性能。working set size*(WSS)是一个重要的概念,它是指计算某个问题答案所需要的内存量。对网页来说,这将是完成短时间内查询所需要的数据。

  1. Hbase缓存需要多少内存是这样计算的:
  2. number of region servers * heap size * hfile.block.cache.size * 0.99

块缓存的默认值是0.25,这代表可获得堆的25%。最后的值(99%)是在eviction被开始后LRU缓存中默认的可接受负载因子。它被包含进这个等式的原因是声称使用了百分百可获得内存是不现实的,因为这将使进程在它载入新块时,阻塞旧块。下面是一些例子:

  • 1个区域服务器,设置1GB的堆内存大小,默认块缓存大小将有可获得块缓存的253MB。
  • 20个区域服务器, 设置8GB堆缓存,默认块缓存将有39.6GB
  • 100个区域服务器,设置24gb堆缓存,默认缓存将有1.16TB。

数据并不仅仅停留在块缓存上,这里有些其他事情是你要考虑的:

目录表 -ROOT-(0.96之前)和hbase:meta表要强制存于块缓存中并要有内存优先级,则会表示他们很难被驱逐。前者从未占用超过几百个字节,而后者能占一些MB(与区域数有关)。

HFiles索引 HFiles是Hbase用来在HDFS中存储数据的文件格式。它包含多层索引,允许Hbase查询数据而不用读取整个文件。索引大小是一个块大小因子(默认64K),即所存储的键和数据数量。对大数据的设置,每个区域服务设置成1GB并不常见,尽管并不是所有的都会在缓存中,因为LRU将驱逐不使用的索引。

Keys 存储下来的值仅仅是图像的一半,因为每个值是和它键(row key, family qulifier, 以及时间戳)一起存储的。

Bloom过滤器 就像HFile索引一样,这些数据结构存储在LRU中。

当前,建议的测量HFiles索引和Bloom过滤器大小的方法是在区域服务器的Web UI中查看,检验相关指标。对keys来说,采样可以使用HFile命令行工具并寻找平均键尺寸指标。从0.98.3之后,可以在BlockCache stats上查看相关细节,在UI中的一个特殊块缓存中度量。

当WSS不适合内存时,通常不使用块缓存。这个例子通常发生在当所有区域服务器块缓存有40GB的可用,而需要处理1TB数据。其中一个原因是,由驱逐产生的改变将导致更多不必要的垃圾收集。这里有两个用例:

  • 完全随机读模式:这个例子是你不会在很短时间内访问同一行两次,这样访问一个块缓存的机会接近为零。在这样表中设置块缓存是浪费内存和CPU周期,同样会产生更多的垃圾需要JVM去收集。
  • 映射一张表:在一个典型的MR job中使用一张表作为输入,每行将仅被读取一次,所以这里没有必要将他们放入块缓存中。Scan对象有将这个设置为off的选项,通过setCaching方法(设为false)。如果你需要快速的随机读访问,你可以仍将块缓存打开。例子将计算服务实时交通表的行数,缓存表中每个块将产生大量变换,将必定会驱逐当前正在使用的数据。

仅缓存元数据块

当我们仅缓存META块时是很有趣的步骤,每次访问都要读取DATA块。如果DATA块放入fscache, 这种可替代方法当对大数据集进行完全随机访问时产生意义。启用这中设置,更改表并将每个列族设置BLOCKCACHE ⇒ ‘false’,仅将这个列族的块缓存关闭,永远不能禁用META块的缓存

69.4.5. Off-heap Block Cache

怎样打开BucketCache BucketCache的通常部署是通过管理类,设置两个缓存层:一个由LruBlockCache实现的L1堆上缓存;第二个由BucketCache实现的L2缓存。默认管理类是CombinedBlockCache。这里链接介绍了CombinedBlockCache实现的缓存策略。简而言之,通过保存元块工作,即在L1堆上LruBlockCache中保存INDEX和BLOOM,L2 BucketCache层中保存DATA块。从1.0版后可以修改这个行为,并要求列族中L1堆内存中即保存meta又保存DATA,通过HColumnDescriptor.setCacheDataInL1(true)设置cacheDataInL1,或在shell中创建并修改列族设置 CACHE_DATA_IN_L1 为真,如:

  1. hbase(main):003:0> create 't', {NAME => 't', CONFIGURATION => {CACHE_DATA_IN_L1 => 'true'}}

BucketCache块缓存可以部署在堆上,堆下或基于文件。通过hbase.bucketcache.ioengine设置。要设置为heap将把BucketCache部署在分配的Java堆内。设置其为offheap将BucketCache部署在堆外,设置为file:PATH_TO_FILE将使BucketCache使用文件缓存(特别是有类似SSD快速I/O时有用)

绕过CombinedBlockCache策略部署一个L1+L2设置是可能的,让BucketCache工作为严格的L2缓存到L1 LruBlockCache。要实现这个设置,将CacheConfig.BUCKET_CACHE_COMBINED_KEY设为false。这种模式下,来自L1的驱逐,块移至L2。当块被缓存时,首先在L1中缓存。当要查找缓存块时,首先在L1中查找,如果没有找到,再去L2中查找。我们叫这种部署方式:Raw L1+l2

其他BucketCache包括:指定一个用来重启的持续缓存。在写入缓存时用多少线程等。

BucketCache Example配置

这个例子提供了一种4GB堆外BucketCache和1GB堆上缓存的配置。

配置在RegionServer上执行。

设置hbase.bucketcache.ioengine 和hbase.bucketcache.size大于零,以启动CombinedBlockCache。我们假定RegionServer设置并运行5G堆内存。如:HBASE_HEAPSIZE=5g

1. 首先,编辑RegionServer的hbase-env.sh并设置HBASE_OFFHEAPSIZE比想要的堆外内存大一点的值,本例中4G(表示为4G)。设置其为5G。将有4G给堆外缓存和1G其他堆外内存应用(除了BlockCache外会有其他功能使用堆外内存,如RegionServer中的DFSClient)

  1. HBASE_OFFHEAPSIZE=5G

2. 下一步,在RegionServer的hbase-site.xml中增加如下配置:

  1. <property>
  2. <name>hbase.bucketcache.ioengine</name>
  3. <value>offheap</value>
  4. </property>
  5. <property>
  6. <name>hfile.block.cache.size</name>
  7. <value>0.2</value>
  8. </property>
  9. <property>
  10. <name>hbase.bucketcache.size</name>
  11. <value>4196</value>
  12. </property>

3. 重启或rolling restart集群,如果有问题查看log

上面的例子中,我们将BucketCache设为4G,配置堆上LruBlockCache有20%的RegionServer堆大小(0.2*5G=1G)。换言之,配置L1 LruBlockCache像正常一样(就像没有L2缓存存在)

HBASE-10641 介绍了给BucketCache的通道配置多个大小的能力,在Hbase0.98之后。要配置多个bucket大小, 配置hfile.block.cache.sizes这个新属性,(替换hfile.block.cache.size),用逗号分隔块大小表,从小到大排序,没有空格。这样的目的是根据数据访问的模式优化bucket大小。下面的例子是配置bucket大小从4096-8192.

  1. <property>
  2. <name>hfile.block.cache.sizes</name>
  3. <value>4096,8192</value>
  4. </property>

69.4.6 Compressed BlockCache 压缩的块缓存

HBASE-11331 介绍了懒惰的BlockCache decompression,比压缩的块缓存要更简单。当压缩块缓存被启用时,数据和编码数据块会以它们的磁盘格式缓存在BlockCache上,而不是在缓存前被解压和解码。

对一个RegionServer 如其比放入缓存,存更多数据,打开这项功能使用SNAPPY压缩,可以增加50%吞吐和改善30%平均延迟,增加垃圾收集80%和增加整体CPU负载2%。See HBASE-11331 for more details about how performance was measured and achieved.如对缓存大小和存储数据大小正合适,或工作负载对于额外的CPU和垃圾收集比较敏感,你将收到很少的好处。

该功能默认关闭,要启用设置所有RegionServer上的hbase-site.xml中的hbase.block.data.cachecompressed为真。

69.5.

当写请求被RegionServer处理时,它们聚集在称为memstore内存存储系统。一旦memstore满了,其内容被写入磁盘作为额外的存储文件。这种行为称为memstore flush。随着存储文件累计,RegionServer将压缩它们成更少但更大的文件。当每次flush或压缩完成,在区域中存储的数据量就改变了。RegionServer参考看区域分割策略来判断是否区域增长到足够大或根据指定的策略应该被分割。按照策略建议,区域分割请求将进行排队。

逻辑上,分割区域是很简单的一件事。我们在区域键空间中找到一个合适的点来将区域分半,然后将区域数据分到两个区域中。然而这个步骤并不简单。当分割发生时,新创建的子区域并不立刻重写数据到新的文件。而是,创建类似符号链接的小文件,叫做Reference files,这个文件根据分割点指向父文件的顶部或底部。reference files可以像常规文件一样使用,但仅有一般记录被考虑。如果没有对父区域的不可变数据文件的引用,该区域只能被拆分。引用文件随着紧缩逐渐被清除,这样区域将停止引用其父文件,并将进一步被分割。

逻辑上,分割区域是很简单的一件事。我们在区域键空间中找到一个合适的点来将区域分半,然后将区域数据分到两个区域中。然而这个步骤并不简单。当分割发生时,新创建的子区域并不立刻重写数据到新的文件。而是,创建类似符号链接的小文件,叫做Reference files,这个文件根据分割点指向父文件的顶部或底部。reference files可以像常规文件一样使用,但仅有一般记录被考虑。如果没有对父区域的不可变数据文件的引用,该区域只能被拆分。引用文件随着紧缩逐渐被清除,这样区域将停止引用其父文件,并将进一步被分割。

尽管分割区域是RegionServer的本地决定,分割进程本身必须遵循许多的因素。RegionServer要在split前后通知Master,更新.META.表这样客户端可以发现新的子区域, 并重新安排HDFS中目录结构和数据文件。分割是一个多任务进程。如果在错误状态下启用回滚,RegionServer将在内存日志中保存执行状态。RegionServer执行分割的步骤见RegionServer Split Process。每一步都使用步骤号作为标签。RegionServer和Master的行为使用红线,而客户端使用绿线。 Figure 1. RegionServer Split Process

1.RegionServer决定在本地分割区域,并准备分割。THE SPLIT TRANSACTION IS STARTED.第一步,RegionServer获取一个共享读锁在表格上,防止分割中表格被修改。然后在zookeeper的/hbase/region-in-transition/region-name下创建一个znode,并设置其状态为SPLITTING

2.Master得知这个znode,因为它有个父region-in-transition znode监视器。

3.RegionServer在HDFS中的父目录region中创建名叫.splits的子目录。

4.RegionServer关闭父区域并在其本地数据结构中将父区域标记为下线。THE SPLITTING REGION IS NOW OFFLINE.这时来到父区域的客户端请求会抛出NotServingRegionException。客户端会重试一些回避。关闭区域被冲洗。

5.RegionServer在.splits下为子区域A和B创建区域目录,并创建必要的数据结构。然后分割存储文件,在这个意义上,它在父区域为每个存储文件创建两个参考文件。这两个参考文件将指向父区域文件。

6. RegionServer在HDFS中创建实际的区域目录,并为每个子区域移动参考文件。

7. RegionServer向.META.表发送一个Put请求,在.META.中设置父区域离线并加入子区域信息。这时,对子区域来说,它们并不是独立的目录。客户端可以看到父区域分裂,如果这时扫描.META.,但并不知道子区域存在,直到它们在.META.中出现。如果Put成功,父将有效地分割。如果在RPC成功之前RegionServer失败,Master和下一个Region Server将清理分割的不正确状态。在.META.更新后了,区域分割由Master控制滚动向前。

8.RegionServer并行打开子区域A和B

9.RegionServer在.META.中加入子节点A和B,以及它管理区域的信息。THE SPLIT REGIONS (DAUGHTERS WITH REFERENCES TO PARENT) ARE NOW ONLINE.这时,客户端发现新的区域并向他们发送请求。客户端在本地缓存.META.目录,但当他们向RegionServer或.META.发出请求时这个缓存将无效,他们将从.META.获取新区域信息。

10. RegionServer更新Znode状态为SPLIT(在/hbase/region-transition/region-name),以使master得知它。如果有需要,均衡器将重新分发子区域到区域服务器中。THE SPLIT TRANSACTION IS NOW FINISHED.

11. 分割之后,.META.和HDFS将仍包含父区域的索引。这些索引将被移除当主紧缩阶段在子区域重写数据文件。master的垃圾收集任务将周期性检查是否子区域索引至父区域文件,如果没有父区域将被移除。

69.6. Write Ahead Log (WAL)

69.6.1.目的

WAL记录了Hbase中所有的数据更改(到文件基础的存储)。在正常操作下,WAL是不需要的因为数据改变移动MemStore到StoreFiles。当然如果 RegionServer在MemStore冲洗之前崩溃或不可得,WAL确保数据改变可以被回放。如果写入WAL文件失败,全部修改数据的操作将失败。

Hbase使用WAL接口实现。通常,每个RegionServer只有一个WAL实例。在记录他们到Memstore生效之前Regionserver记录其Puts和Deletes。

WAL在HDFS中的/hbase/WALs/目录每个子区域有一个子目录。

69.6.2. MultiWAL

每个区域有一个WAL的情况下,RegionServer必须连续写WAL,因为HDFS文件必须连续。这导致WAL成为性能瓶颈。

Hbase1.0后支持MultiWal。MultiWAL让RegionServer并行地写多个WAL流,通过使用HDFS实例下的多管道,这将增加写时的整体吞吐量。这种并行化是通过区域分区传入的编辑完成的。因此,目前的实现将不会有助于提高吞吐量到一个单一的区域。

RegionServers 使用原WAL实现,那些使用多WAL实现的可以实现从每个WALs设置恢复,所以通过滚动重启,零下线时间配置更新是可行的。

Configure MultiWAL 为RegionServer配置MultiWAL,设置属性值hbase.wal.provider为multiwal,在下面的XML中:

  1. <property>
  2. <name>hbase.wal.provider</name>
  3. <value>multiwal</value>
  4. </property>

重启RegionServer使配置生效。

要关闭MultiWAL,不要设置这个属性,然后重启RegionServer。

69.6.3. WAL Flushing

TODO(descirbe)

69.6.4. WAL Splitting

一个区域服务器服务许多个区域,RegionServer中所有的区域共享同一个被激活的WAL文件。WAL文件中每个edit包含其属于哪个region的信息。当区域被打开,WAL文件中属于那个区域的edits需要replayed.因此,WAL文件中的edits必须被区域分组,这样特定设置可以被重放已在特定区域中重新产生数据。WAL edits分组进程叫做log splitting。这是在区域服务器挂掉时,用来恢复数据的关键进程。

log splitting由HMaster在集群启动时完成,或通过调用ServerShutdownHandler,在区域服务器关闭时完成。所以一致性是可以保证的,受影响的区域是不能重新别使用,直到数据被恢复。所有WAL edits需要被恢复和重放在区域再次提供服务之前。最终,被log splitting影响的区域只有在该进程完成后才会可用。

Procedure: Log Splitting, Step by Step

1. /hbase/WALs/,,目录被重命名。

重命名目录十分重要,因为区域服务器依然是启动状态并会接收请求,虽然HMaster以为其下线了。如果区域服务器没有立刻回应,且没有心跳回应到ZooKeeper通信中,HMaster将以为RegionServer失败而中断。重命名日志目录确保存在的、有效的WAL文件将仍然可以被活的但忙碌的RegionServer使用,而不会被随机情况更改。

新目录以下面的形式重命名:

  1. /hbase/WALs/<host>,<port>,<startcode>-splitting

重命名的目录可能会向下面这样子:

  1. /hbase/WALs/srv.example.com,60020,1254173957298-splitting

2. 每个日志文件都在被拆分,一次一个。

日志拆分器一次读取日志文件中的一个编辑条目。并将每个编辑条目放入缓存中,使编辑的区域一致。同时,分割器启动几个写线程。写线程拿起相应的缓冲区,并写edit条目到缓存中.临时编辑目录用以下命名形式存储在磁盘中:

  1. /hbase/<table_name>/<region_id>/recovered.edits/.temp

这个文件被用于存储这个区域所有WAL日志中的编辑。在日志分隔完成后,.temp文件被重命名第一个被写入文件中的序列ID。

要决定是否所有的编辑被写入,序列ID会被拿来和上一个被写入HFile的编辑序列比较。如果上次编辑的序列大于等于文件名中的序列ID,很明显来自edits文件的写入被完成了。

3.在日志分隔完成后, 每个受影响的区域被分发给区域服务器。 当该区域被打开时,在recovered.edits文件夹中查找恢复的编辑文件。如果任何这样的文件存在,它们将被读取编辑replayed并保存到Memstore。在所有的edits文件被replayed之后,MemStore中的内容被写入磁盘(HFile),然后编辑文件被删除。

处理日志分隔过程中的错误

如果设置hbase.hlog.split.skip.errors为真, 错误会用以下方法处理:

  • 在分割过程中所有错误将被记录下来。
  • 问题WAL日志将被移进hbase root根目录下的.corrupt目录中。
  • WAL进程将会继续。

如果hbase.hlog.split.skip.errors设为false,默认的,异常将会被propagated,分割将被记为失败。We need to do more than just fail split if this flag is set

当分割一个坏掉的区域服务器WAL时,怎样处理EOFExceptions?

当EOFException在分割日志时发生,分割会继续即使hbase.hlog.split.skip.errors被设为false。在读取文件集中的最后一个要分割的日志时,EOFException是有可能发生的,因为区域服务器在崩溃时可能正处于写记录的过程中。

日志分割阶段的性能提升

WAL日志分割和恢复可能是资源密集型且会花费很长时间,根据崩坏区域的数量以及区域的大小。

打开或关闭分布式的日志分割 分布式的日志进程从0.92之后启用的。由hbase.master.distributed.log.splitting属性控制,可以设为true或false,默认为true。

一步一步:分布式日志分割

在配置分布式日志分割后,HMaster控制进程。Hmaster记录每个在日志分割进程的区域服务器,而实际的分割工作由区域完成。The general process for log splitting, as described in Distributed Log Splitting, Step by Step still applies here.

1. 如果分布式日志进程被启用,当集群启动时HMaster创建一个分割日志管理员实例。

  • 分割日志管理器管理所有需要扫描和分割的日志文件。
  • 分割日志将所有在ZooKeeper分割日志节点中的日志置为任务
  • 可以查看分割日志内容通过使用下面zkCli命令。输出例子如下:

    1. ls /hbase/splitlog
    2. [hdfs%3A%2F%2Fhost2.sample.com%3A56020%2Fhbase%2F.logs%2Fhost8.sample.com%2C57020%2C1340474893275-splitting%2Fhost8.sample.com%253A57020.1340474893900,
    3. hdfs%3A%2F%2Fhost2.sample.com%3A56020%2Fhbase%2F.logs%2Fhost3.sample.com%2C57020%2C1340474893299-splitting%2Fhost3.sample.com%253A57020.1340474893931,
    4. hdfs%3A%2F%2Fhost2.sample.com%3A56020%2Fhbase%2F.logs%2Fhost4.sample.com%2C57020%2C1340474893287-splitting%2Fhost4.sample.com%253A57020.1340474893946]

输出包含某些非ASCII字符。当解码时,其看起来更简单:

  1. [hdfs://host2.sample.com:56020/hbase/.logs
  2. /host8.sample.com,57020,1340474893275-splitting
  3. /host8.sample.com%3A57020.1340474893900,
  4. hdfs://host2.sample.com:56020/hbase/.logs
  5. /host3.sample.com,57020,1340474893299-splitting
  6. /host3.sample.com%3A57020.1340474893931,
  7. hdfs://host2.sample.com:56020/hbase/.logs
  8. /host4.sample.com,57020,1340474893287-splitting
  9. /host4.sample.com%3A57020.1340474893946]

上面是日志分割任务表,列出了要被扫描和分割的WAL文件名。

2. 分割日志管理器检测日志分割任务和worker。

分割日志管理器复制下面这些正在运行的任务:

  • 一旦管理器发布所有的任务到分割点上, 它就检测这些任务节点并等待它们被处理。
  • 检查队列中是否有挂掉的日志分割worker。如果发现反应缓慢worker负责的任务,它将重新提交这一任务。如果重新提交因某些ZooKeeper出错而失败,挂掉的worker将被放入队列等待重试。
  • 检查有没有被分发的任务。如果找到, 它将创建一个短暂的再扫描节点,使每个分割worker都被通知到重扫描为分发任务,通过 nodeChildrenChanged ZooKeeper event.
  • 检查被分配但过期的任务,找到后,将其状态变回TASK_UNASSIGNED,使其可以被重试。很有可能,某些任务被发给了slow workers,或者它们已经被完成了。这不是一个问题,因为日志分割任务等幂性属性,即同样的日志分割任务可以被处理多次而不引起任何问题。
  • 分割日志管理器时常监视HBase分割日志节点。如果哪个分割日志任务节点数据被改变,管理器将检索节点数据。节点数据包含当前任务状态。可以使用zkCli的get命令来检索当前任务状态。下面例子的输出中,第一行显示当前任务没有被分配。

    1. get /hbase/splitlog/hdfs%3A%2F%2Fhost2.sample.com%3A56020%2Fhbase%2F.logs%2Fhost6.sample.com%2C57020%2C1340474893287-splitting%2Fhost6.sample.com%253A57020.1340474893945
    2. unassigned host2.sample.com:57000
    3. cZxid = 0×7115
    4. ctime = Sat Jun 23 11:13:40 PDT 2012
    5. ...

在数据改变的任务状态的基础上,分割日志管理器会做以下事情:

  • 如果没有分发,将重新提交任务。
  • Heartbeat the task if it is assigned
  • 如果任务已经失败,重新提交或放弃任务。
  • 如果任务带有错误的完成,重新提交或放弃任务。
  • 如果由于错误,任务无法完成,重新提交或放弃任务。
  • 如果其成功完成或失败,就删除任务。

3. 每个区域服务器的分割日志worker执行日志分割任务。

每个regionserver运行着一个叫做split log worker的守护进程,其工作是进行日志分割。该进程随着RegionServer启动而启动,注册自己来监视Hbase znodes。如果任何分割日志的znode children改变,它唤醒一个睡眠中的worker线程来处理更多任务。如果某个守护进程的当前任务的节点数据被改变了,守护进程会查看该任务是否被其他进程接管。如果这样的话,worker线程停止当前任务。

worker时常监测分割日志znode,当新任务来到时,分割日志worker检索每个任务路径并检查每一个知道发现无人认领的任务, 其将尝试认领。如果认领成功,将尝试执行任务并在分割输出的基础上更新任务的state属性。这时, split log worker查找其他未被认领的任务。

How the Split Log Worker Approaches a Task

  • 查询任务状态,当任务状态为TASK_UNASSIGNED时,采取行动。
  • 如果状态为TASK_UNASSIGNED ,worker将试着将任务状态设为TASK_OWNED由其自己。如果设置失败,其他worker将试着获取这个任务。如果任务仍处于未分配状态,分割日志管理器也会要求所有worker随后再次扫描。
  • 如果worker成功获取任务的所有权,其将尝试再次获取状态来确保它真的异步获取它。同时,其启动一个分割任务执行线程来做真正的工作:

    1、获取HBase根目录,在根目录下创建临时目录将日志文件分割到临时文件中

    2、如果分割任务成功,将设置任务状态为TASK_DONG

    3、如果worker捕捉到一个enexpected IOException, 任务状态将设置为TASK_ERR

    4、如果任务关闭了, 设置任务状态为TASK_RESIGNED.

    5、如果任务由其他worker接管,仅记录它。

4. 分割日志管理器监测未完成任务

当所有任务成功完成时,分割日志管理器返回。如果所有任务被完成但带有某些错误,分割日志管理器将抛出异常使日志分割可以被重试。由于异步实现,在非常少的情况中,分割日志管理会失去对某些完成任务的追踪。出于这个原因,其会周期性的在其任务表或ZooKeeper中检查仍未完成的任务。如果没有发现,将抛出一个异常,使日志分割可以被立刻重试而不是停在那等着某些不会发生的事情。

Distributed Log Replay

在一个RegionServer失败后,失败的区域将被分发到其他区域服务器上,在ZooKeeper中会被标记为“recovering”。一个分割日志worker直接从失败的RegionServer的WAL到区域的新位置回放编辑。当区域在”recovering”状态时,可以被写入但不能被读取(包括Append和Increment)、region splits或merges。

分布式日志回放继承自 Enabling or Disabling Distributed Log Splitting 框架。通过直接回放WAL edits到另外一个区域服务器上而不是创建recovered.edits文件。其比单独分布式日志分割提供下面的优势:

  • 它消除了读写大量recovered.edits文件的开销。在RegionServer恢复的同时,成千上万的recovered.edits被创建时不常见的。许多小的随机写入可以降低整个系统的性能。
  • 当区域在恢复状态时,它允许写入。一个区域重新接受写只花费很短时间。

Enabling Distributed Log Replay

设置属性hbase.master.distributed.log.replay为true,打开分布日志回放,0.99后该设置默认为true

也需要启用HFile v3(为0.99版后默认的HFile format)。分布式日志回放对滚动更新是不安全的。

69.6.5.关闭WAL

可以在特定的情况下关闭WAL,以获得更好的性能。当然,关闭WAL会使数据处于风险中。只有在批量加载时,才是建议的情况。这是因为,在问题发生时,批量加载可以重新运行,没有丢失数据的风险。

通过调用客户端field Mutation.writeToWAL(false)来disable WAL 使用Mutation.setDurability(Durability.SKIP_WAL)设置 field值 而使用Mutation.getDurability()获得filed值。没有仅在一张表上关闭WAL的方法。

70.区域

区域是表可用性和分布的基本元素,是由每个列族的Store组成的。对象的层次结构如下:

  1. Table (HBase 表)
  2. Region (表区域)
  3. Store (Store 表的每个区域的每个列族)
  4. MemStore (表每个区域每个StoreMemStore)
  5. StoreFile (表每个区域每个StoreStoreFiles)
  6. Block (在表每个区域每个Store中的一个StoreFile中的块)

For a description of what HBase files look like when written to HDFS, see Browsing HDFS for HBase Objects.

70.1.区域数量的考虑

通常,HBase设计为每个服务器运行相对大的(5-20GB)少量区域(20-200)。这是出于下面的考虑:

为什么要保持区域计数低

通常情况下,想要保持Hbase上的区域数低,出于许多原因。通常每个区域服务器负责大概100个区域时能够最好的处理结果。下面是一些波爱吃区域计数低的理由:

1. 每个MemStore需要2MB MSLAB(MemStore-local allocation buffer)(即每个区域每个family 2MB)。1000个具有两个families的区域将使用3.9GB堆,这还不是要存储的数据。NB:2MB是配置值。

2. 如果以相同的速度填充所有的区域,全局内存的使用使当有许多区域要强制小缓冲,最终产生压缩。(the global memory usage makes it that it forces tiny flushes when you have too many regions which in turn generates compactions.)重写相同的数据几十次是你想要的最后一件事。一个列子是填充1000个相同区域,我们考虑全局MemStore使用5GB的下界(区域服务器将有一个大的堆内存)。一旦到达5GB,其将强制flush最大的区域,在这点上,它们应该几乎有大概5mb数据,它会flush那个之后被插入的5mb数量, 它将flush另外的有一点多于5MB数据的其他区域,等等。这是当前主要区域数量的限制因素。

3. master对大量区域很敏感,将花费许多时间分配它们并分批移动它们。原因是ZK负担很重,那时这并非异步的。

4. 在旧的Hbase版本中(pre-HFile v2, 0.90 and previous),在少量RS上的大量区域会导致store file 索引增加,堆内存使用增加,潜在的内存压力或在RS上的OOME

区域的数量对MR job的影响也是个问题;通常每个区域只有一个Mapper。因此,一个RS仅有5个区域可能对于MR job来说无法获得足够数量的任务。而1000个区域将产生太多的任务。

70.2区域-区域服务器分配

本小节介绍如何将区域分配给区域服务器

70.2.1 启动

当HBase启动时,区域按以下方法被分配:

1.manager启动时唤醒AssignmentManager

2.AssignmentManager在hbase:meta中查看已经存在的区域分配情况。

3.如果某个区域分配仍然是有效的(如区域服务器仍然在线),那么该分配将被保留。

4.如果分配无效了,那么启动LoadBalancerFactory 来分配区域。负载平衡将区域分配给区域服务器。

5.hbase:meta随着区域服务器分配更新 and the RegionServer start codes (start time of the RegionServer process) upon region opening by the RegionServer

70.2.2. Failover

当区域服务器挂掉时,

1. 区域立刻无法使用因为区域服务器下线了。

2. Master将检测到RegionServer挂了。

3. 区域分配将被判为失效,将像刚启动的流程一样重新进行分配

4. 传输队列中的将被重试,而不是丢失。

5. 操作将转移到新区域服务上在下面的时间内:

  1. ZooKeeper session timeout + split time + assignment/replay time

70.2.3 区域负载平衡

区域可周期性的被LoadBalancer移动

70.2.4. 区域状态转换

Hbase维护每个区域的状态并将这些状态保存在hbase:meta中。hbase:meta区域本身的状态在ZooKeeper中保存。可以在Master Web UI中查看区域状态的转换。下面是可能的区域状态

Possible Region States

  • OFFLINE: 区域离线并没有打开

  • OPENING: 区域在启动过程中

  • OPEN: 区域出于打开状态,RegionServer通知主机
  • FAILED_OPEN: RegionServer打开区域失败
  • CLOSING: 区域在被关闭的过程中
  • CLOSED: RegionServer已经关闭区域并通知主机
  • FAILED_CLOSE:RegionServer关闭区域失败
  • SPLITTING: RegionServer通知Master区域正在分割
  • SPLIT:区域服务器通知master分割已经完成
  • SPLITTING_NEW:分割正在创建区域
  • MERGING: RegionServer通知master区域正与另外一个区域进行合并
  • MERGED:RegionServer通知master区域被合并了 区域状态转换

图2:区域状态转换

Graph Legend图例

  • Brown: 离线状态,一个特殊的可能是瞬态的状态(在关闭之后打开之前),中断(不可用表的区域)或初始(新创建表的区域)
  • Palegreen淡绿色:上线状态即区域可提供服务请求
  • Lightblue:转移状态
  • Red:失败状态,需要ops(运维)注意
  • Gold:区域分割或合并的终止状态
  • Grey:分割或合并的初始状态

过程状态介绍

1.Master将区域从OFFLINE状态变为OPENING状态,并试着将区域分发给RegionServer。RegionServer可能收到也可能没有收到打开区域的请求。master不断向RegionServer发打开区域请求直到RPC通过或master runs out of retries。在RegionServer收到open region请求后,区域服务器开始打开区域。

2. If the master is running out of retries,master通过将状态置为CLOSING来阻止RegionServer打开区域。

3. 在RegionServer打开区域后,其将继续尝试通知master直到master将区域状态置为OPEN状态,并通知RegionServer.区域已经打开了。

4. 如果区域服务器不能打开区域,它将通知master。maaster将区域状态置为CLOSED状态,并试着在不同的区域服务器中打开这个区域。

5. 如果master不能在某些特定数量的区域上打开区域,它将这个区域标为FAILED_OPEN状态, 在操作者利用Hbase shell 介入或服务器挂掉之前不会采取其他行动。

6. master将区域状态从OPEN置为CLOSING状态。持有区域的区域服务器可能收到也可能收不到关闭区域的请求。master会不断发送关闭请求,直到达成RPC或master runs out of retries。

7. 如果区域服务器不在线,或抛出NotServingRegionException, master将区域变为OFFLINE状态,并将重发给其他区域服务器。

8. 如果区域服务器在线,但在master超过重试次数后不可达,master将区域移动为FAILED_CLOSE状态, 在操作者利用Hbase shell 介入或服务器挂掉之前不会采取其他行动。

9. 如果区域服务器收到关闭区域请求,它将关闭区域并通知master。master将区域状态变为CLOSED状态,并重发区域到其他区域服务器上。

10. 在分发一个区域后,如果其状态为CLOSED,master将自动将其状态置为OFFLINE状态。

11. 当区域服务器将要进行区域分割时,它将通知master。master将待分割的区域状态由OPEN变为SPLITTING,并增加两个新的区域(被在RegionServer上创建的)这两个区域是以SPLITING_NEW 状态初始化的。

12. 在通知master之后,区域服务器开始分割区域。一旦超过没有回复的点,区域服务器再次通知master,使master更新hbase:meta表,master不会更新区域的状态,直到其被通知split被完成。如果分割成功,区域状态由SPLITTING变为SPLIT,而两个新的区域由SPLITTING_NEW 变为OPEN状态。

13. 如果分割失败,分割区域状态由SPLITTING 变为OPEN,两个新创建的又SPLITTING_NEW 变为OFFLINE状态。

14. 当区域服务器要合并两个区域时,它会先通知master。master将两个待合并的区域状态由OPEN变为MERGING状态。并在RegionServer增加一个新的区域来保留合并区域的内容。这个新区域将初始化为MERGING_NEW 状态。

15. 在通知master之后,RegionServer开始合并两个区域,一旦达到没有回复的点,区域服务器将通知master可以更新META. 当然,直到master得知合并完成,其才会更新master的状态。如果合并成功,两个合并的区域的状态由MERGING变为MERGED,新的区域状态从MERGING_NEW变为OPEN状态。

16. 如果合并失败,两个merging区域将从MERGING 状态变回OPEN状态,保留合并区域内容的新区域将由MERGING_NEW 变为OFFLINE

17. 对状态为FAILED_OPEN和FAILED_CLOSE的区域,master将尝试关闭它们,当它们同过HBase shell操作重试时。

70.3 区域-区域服务器locality

随着时间推移,Region-RegionServer locality通过HDFS块复制实现。当选择写备份位置时,HDFS客户端默认情况下为做下面操作:

1. 首先复制被写到本地节点

2. 第二,复制被写到另外机架的随机节点上

3. 第三,复制被写到同一机架上,作为第二副本,随机选择的不同节点上

4. 第四, 副本被写入到集群中随机节点上。

因此, 最终在flush或compaction之后,Hbase实现区域的locality。在区域服务器挂掉的情况下,区域服务器可能收到非本地的存储文件(因为没有副本是在本地上的), 然而,作为新写入到区域的数据,或表被压缩和StoreFile被重写,他们将变成新Regionserver的本地数据。

70.4. 区域分割

当达到配置的阈值后,区域进行分割。 Below we treat the topic in short. For a longer exposition, see Apache HBase Region Splitting and Merging by our Enis Soztutar.

分割在区域服务器上独立运行。如, master不会参与。区域服务器分割区域,使被分割区域下线然后在hbase:meta中增加子区域,在父RegionServer上打开子区域,然后将分割报告给Master. See Managed Splitting for how to manually manage splits (and for why you might do this).

70.4.1.自定义分割策略

可以使用自定义分割策略覆盖默认策略,典型的自定义分割是扩展Hbase默认的分割策略:IncreasingToUpperBoundRegionSplitPolicy.

70.5. 手动区域分割

可以手动分割表格,在表格创建时(pre-splitting)或在在随后的时间作为一种管理行为进行。可以选择分割区域出于以下原因。也可能有其他合法的原因,但是需要手动分割表格可能也指向了某个表格设计上的问题。

手动分割表格的原因

  • 按时间或其他类似的算法进行分类的数据会将新数据分在表格最后。这意味着运行最后区域的区域服务器一直在负载之下,而其他Region Server可能处于空闲状态。
  • 在表中某个区域中开发某个没有预计到的热点。例如,某个追踪Web搜索的应用可能被淹没在一堆名人新闻的搜索中。
  • 集群中RegionServer数量大量增长之后,使负载快速分散。
  • 在一次可能导致不常见的或不平衡的区域负载出现的批量加载之前

70.5.1决定分割点

手动分割表的目标是增加整个集群负载平衡的机会,即好的rowkey设计不会使集群负载不平衡的情况出现。要记住,分割区域的方法是独立于数据角色的。可能你已经知道最好的分割表格的方法。如果不知道,分割表格的方法依赖于表键值的样子。

字母数字RowKey 如果行键以字母或数字开始,可以在字幕或数字的边界进行分割。例如,下面命令创建的表区域在每个元音上分裂,所以第一片区域为A到D,第二片区域从E到H,第三片区域是I到N,第四片区域是O到V,第五片区域是U到Z。

使用自定义算法 区域分割工具由Hbase提供,并使用分割算法来为你决定分割点,需要提供算法,想要得到的区域数目以及列族作为参数。其提供了两种分割算法,第一个是HexStringSplit算法,即假定行键值为16进制字符串。第二种,UniformSplit,即假定行键值为随机字节数组。你也可能需要开发你自己的分割算法,使用提供的一种作为模型。

70.6在线区域合并

Master和RegionServer会参与到在线区域合并中。客户端发送合并RPC请求到master,然后master将区域移动到同一RegionServer上,在这个RegionServer上有更中负载的区域。最终,master将合并请求发送到这个RegionServer上,然后其上进行合并。于区域分裂相似,区域合并在RegionServer上以本地业务运行。线使区域下线然后在文件系统上合并两个区域,自动从hbase:meta中删除合并区域,并加入合并好的区域,在RegionServer上打开合并好的区域并向Master报告合并。

HBase shell的区域合共举例:

  1. $ hbase> merge_region 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME'
  2. $ hbase> merge_region 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME', true

这是一个异步操作并立刻获得返回值并不等待合并完成。传递true作为可选的第三个参数将强制进行合并。正常情况下,联通的区域可以被合并。 force参数覆盖这种行为并仅供专家使用。

70.7 Store存储

一个存储仓库负责一个MemStore以及0个或多个more StoreFiles(HFiles).一个存储对应于一个给定区域的表的列族。

70.7.1 MemStore

MemStore 保存Store的内存修改,Cells/KeyValues的修改.当请求刷新时,当前的MemStore被移动到一个快照并被清除。HBase继续服务来自新MemStore的edits并备份快照,直到刷新成功。这时,快照被丢弃。注意当缓存刷新发生时,属于同一个区域的MemStore将全部被刷新。

70.7.2 MemStore刷新

MemStore刷新可以在下面任意条件下被触发: 最小的刷洗单元是区域,而不是单独的MemStore级。

1. 当某个MemStore达到了hbase.hregion.memstore.flush.size中配置的大小时,所有属于这个区域的MemStore将被刷洗到硬盘中。

2. 当所有MemStore达hbase.regionserver.global.memstore.upperLimit中配置的值时,来自不同区域的MemStore将被刷洗到磁盘中来减少RegionServer中MemStore的使用。

冲洗是按着区域的MemStore使用顺序的降序进行。

区域的MemStore冲洗将等到整体MemStore使用降到hbase.regionserver.global.memstore.lowerLimit之下再进行。

3. 当在一个给定区域服务器的WAL日志项数达到hbase.regionserver.max.logs中指定的数目时,来自不同区域的MemStore将被冲洗到磁盘中来减少Wal中的日志数。刷洗是按照时间顺序进行的。

具有最久的MemStore区域第一个被冲洗直到Wal计数降到hbase.regionserver.max.logs之下。

70.7.3 扫描

  • 当客户端向一张表发起扫描时,HBase生成RegionScanner对象,每个区域一个,来响应扫描请求。
  • RegionScanner对象包含StoreScanner对象表,每个列组一个。
  • 每个StoreScanner对象又包含一个StoreFileScanner对象,对应每个Storefile和相应的列组文件,以及每个MemStore一个KeyValueScanner对象表
  • The two lists are merged into one, which is sorted in ascending order with the scan object for the MemStore at the end of the list
  • 当StoreFileScanner对象被构建时,将与MultiVersionConcurrencyControl read point(即当前的memStoreTS) 联系起来,过滤出在readpoint上的任何新的更新。

70.7.3 StoreFile(HFile)

StoreFile是存储数据的地方。

HFile Format The HFile file format is based on the SSTable file described in the BigTable [2006] paper and on Hadoop’s TFile (The unit test suite and the compression harness were taken directly from TFile). Schubert Zhang’s blog post on HFile: A Block-Indexed File Format to Store Sorted Key-Value Pairs makes for a thorough introduction to HBase’s HFile. Matteo Bertozzi has also put up a helpful description, HBase I/O: HFile.

For more information, see the HFile source code. Also see HBase file format with inline blocks (version 2) for information about the HFile v2 format that was included in 0.92.

HFile Tool 可以使用org.apache.hadoop.hbase.io.hfile.HFile工具,来查看文本化的HFile 内容版本。使用下面命令来查看用法:

  1. $ ${HBASE_HOME}/bin/hbase org.apache.hadoop.hbase.io.hfile.HFile

For example, to view the content of the file hdfs://10.81.47.41:8020/hbase/TEST/1418428042/DSMP/4759508618286845475, type the following:

  1. $ ${HBASE_HOME}/bin/hbase org.apache.hadoop.hbase.io.hfile.HFile -v -f hdfs://10.81.47.41:8020/hbase/TEST/1418428042/DSMP/4759508618286845475

使用-v来看HFile总结。看HFile工具的其他用法。

StoreFile Directory Structure on HDFS

For more information of what StoreFiles look like on HDFS with respect to the directory structure, see Browsing HDFS for HBase Objects.

70.7.5.块

StoreFiles由块组成。块大小在每个列族基础上的配置。

压缩在StoreFilsez中块等级上进行。For more information on compression, see Compression and Data Block Encoding In HBase

70.7.6.键值

键值类是Hbase中数据存储的核心。 键值封装了一个字节数组,传递偏移量和长度来指定哪里开始解释内容作为键值。

字节数组中的键值格式:

  • 键长度
  • 值长度

键可以进一步解压为

  • 行长度
  • 列组长度
  • 列族
  • 列族名
  • 时间戳
  • 键类型

键值实例不是按块分裂。例如,如果有一个8MB键值,即使块大小是64kb,这个键值将被以一个块读取。

Exmaple 为了强调上面几点,测试对同一行不同列两个Put操作的结果:

  • Put #1: rowkey=row1, cf:attr1 = value1
  • Put #2: rowkey=row2, cf:attr2 = value2

即使这些是对同一行,键值为每列创建:

Key portion for Put #1:

即使这些是对同一行,键值为每列创建:

Key portion for Put #1:

  • 行长度——-4
  • 行————-row1
  • 列族长——-2
  • 列族———-cf
  • 列属性———attr1
  • 时间戳———Put的服务器时间
  • 键类型———Put

Key portion for Put #2:

  • 行长度——-4
  • 行————-row2
  • 列族长——-2
  • 列族———-cf
  • 列属性——-attr2
  • 时间戳——-Put的服务器时间
  • 键类型——-Put

理解行键、列族、以及列是由KeyValue实例组装而成的,这些标识符越长,键值越大。

70.7.7 Compaction

两义性术语

  • StoreFile是Hfile的facade。依据compaction,使用StoreFile似乎以成为过去。
  • Store与列族是同一事物。StoreFile与一个Store或列族相关。
  • If you want to read more about StoreFiles versus HFiles and Stores versus ColumnFamilies, see HBASE-11316.

当Memstore达到设定的大小(hbase.hregion.memstore.flush.size),它将冲刷缓存内容到一个StoreFile中。Store中的StoreFile数量随着时间增加。Compaction是一种在一个Store中减少StoreFile数量的操作,通过合并实现,是为了提高读操作性能的操作。compaction是资源密集型操作,可以提升或阻碍性能这取决于许多因素。

压实可以分成两类:minor和major。这两类在下面方面不同:

Minor compactions通常选择少量小的、连接的StoreFiles,重写它们为一个单独的StoreFile。因为潜在的副作用,Minor Compaction不过滤。删除或过期的版本。对某个Store来说,minor compaction的结果为少量大的StoreFiles

major compaction的最终结果是每个Store一个StoreFile.Major compaction也会处理删除标志和最大版本。

紧缩和删除

当显式删除发生在Hbase中时,数据没有被真正被删除。Instead,一个tombstone标志位被写上。这个tombstone标志位阻止数据被查询。在主紧缩阶段,数据被真正删掉,tombstone标志被从StoreFile上移除。如果删除是由于过期的TTL,将不会创建tombstone。Instead,过期的数据将被过滤掉,不会被写回紧缩的StoreFile。

紧缩与版本

当创建一个列族时,可以指定保留的最大版本号,通过HColumnDescriptor.setMaxVersions(int versions)方法。默认值是3.如果存在比指定的最大版本更多的版本,超出的版本将被过滤掉,且不会写回紧缩的StoreFile。

理论上,主紧缩可提升性能。当然,在高负载的系统上,主紧缩可能需要一个不恰当的资源并对性能造成不利的影响。默认配置下,主紧缩计划每7天一个周期自动进行。这有时对生产中的系统不太适合。可以手动管理主紧缩。

紧缩不操纵区域合并。

紧缩策略—HBase 0.96x及之后版本

压缩大的StoreFile或一次压缩太多StoreFiles可能会导致IO负载超过集群能够承受的IO量而不会导致性能问题。HBase选择哪个storeFiles包含到紧缩的方法叫做紧缩策略。

0.96之前,只有一种紧缩策略。原来的策略依然可用,即RatioBasedCompactionPolicy。默认的新紧缩策略,叫做ExploringCompactionPolicy,在0.94或0.95中作为后面的补充,在0.96之后作为默认策略。总的来说,ExploringCompactionPolicy方法试着选择最好的可能的StoreFiles组,并用最少的工作量进行压缩。而RatioBasedCompactionPolicy 试着选择第一套满足标准的Storefile组。

不管使用的压缩策略,文件选择是由几个配置参数控制,并发生在一个多步骤方法中。这些参数将在下文中解释,将在一个表中总结它们的描述、默认值和改变它们的影响。

Being Stuck

当MemStore太大时,需要flush其内容到StoreFile中。当然,一个Store只能有hbase.hstore.blockingStoreFiles个文件,所以MemStore需要等StoreFiles数量被一个或多个紧缩减少。However,如果Memstore大小比hbase.hregion.memstore.flush.size还大时,其不能flush它的内容到StoreFile中。如果MemStore太大,而StoreFile太多,算法上就成为“stuck”。紧缩算法检查“stuck”的情形,并提供机制来缓解它。

The ExploringCompactionPolicy Algorithm 在选择设置之前,The ExploringCompactionPolicy Algorith考虑每个可能相邻的StoreFiles设置,以使紧缩受益最大。

ExploringCompactionPolicy策略效果特别好的一种情况是当进行批量数据加载时,批量加载创建比原有StoreFile(保存了比批量加载的数据更旧的数据)更大的StoreFile。每次需要紧缩时,这个可以发HBase选择执行主紧缩,并导致额外的开销。在ExploringCompactionPolicy中,主紧缩发生次数要少很多因为minor compaction更常发生。

通常,ExploringCompactionPolicy适用大多数情形,所以称为默认紧缩策略。

策略的逻辑可以在hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/compactions/ExploringCompactionPolicy.java中查看。下面是其逻辑的概述。

1. 列出所有Store中的StoreFiles。算法剩下的部分是过滤出HFiles的子集来选出那些要进行紧缩的。

2.如果这是用户发起的一个紧缩,尝试执行请求的紧缩类型,不管通常的选择。注意,有时候虽然用户发起一次主紧缩,也可能不执行主紧缩。这是因为不是所有的在列族中的StoreFile可用来紧缩或因为列族中有太多的Stores。

3. 一些StoreFile被自动排除在考虑之外,包括:

  • 比hbase.hstore.compaction.max.size设置大的StoreFile
  • 批量加载的StoreFile并指定被排除在紧缩之外。You may decide to exclude StoreFiles resulting from bulk loads, from compaction.要这样做,在批量加载之前指定hbase.mapreduce.hfileoutputformat.compaction.exclude这个参数。

4. 遍历第一步中的列表,提取出所有潜在的StoreFiles组来紧缩到一起。一个潜在的组是由hbase.hstore.compaction.min个表中连续StoreFiles组成。对于每个组,进行一些健全检查,并弄清楚这样是否可以实现最好的紧缩:

  • 如果组中StoreFile的数量少于hbase.hstore.compaction.min或多于hbase.hstore.compaction.max,不要考虑它。
  • 比较这套StoreFile的大小与表中可以发现的最小可能的紧缩操作。如果这套StoreFile的大小可以代表可以进行的最小紧缩,store it to be used as a fall-back if the algorithm is “stuck” and no StoreFiles would otherwise be chosen. See Being Stuck.
  • 对组中每个StoreFile进行大小基础上的健全性检查.
  • 如果这组StoreFile依然在考虑范围之内,将其与之前最好的紧缩进行比较。如果更好,用这个替换之前的。
  • 当表中全部潜在的紧缩被处理过之后,执行找到的最好紧缩。如果没有选出,但有多个StoreFiles,即假定算法stuck了,这样的话,执行第三步中找到的最小紧缩。

RatioBasedCompactionPolicy Algorithm RatioBasedCompactionPolicy Algorithm是0.96之前唯一的紧缩策略。尽管ExploringCompactionPolicy被引入 HBase 0.94 and 0.95.使用RatioBasedCompactionPolicy 而不是ExploringCompactionPolicy,设置hbase-site.xml中的hbase.hstore.defaultengine.compactionpolicy.class为RatioBasedCompactionPolicy 。如果换回ExploringCompactionPolicy,移除该设置。

下面介绍RatioBasedCompactionPolicy中用来选择进行紧缩的StoreFile的算法。

1.第一步,是创建包含所有紧缩候选的表格。A list is created of all StoreFiles not already in the compaction queue,and all StoreFiles newer than the newest file that is currently being compacted. StoreFile表按照Sequence Id排列。Sequence Id是在Put被追加到write-ahead log时产生,并存于Hfile的元数据中。

2. 检查算法是否stuck。

3. 如果紧缩时用户发起的,试着执行请求的紧缩类型。如果所有Hfile不可用或存在太多StoreFile可能无法进行主紧缩。

4. 某些StoreFile会被自动排除在考虑之外。包括:

  • StoreFiles大于hbase.hstore.compaction.max.size
  • 批量加载操作创建StoreFile时,指定排除的对象。You may decide to exclude StoreFiles resulting from bulk loads, from compaction. To do this, specify the hbase.mapreduce.hfileoutputformat.compaction.exclude parameter during the bulk load operation.

5. 主紧缩阶段允许的最大的StoreFiles数量是由hbase.hstore.compaction.max参数控制的。如果表中包含这个数量的StoreFiles,minor紧缩将被执行即使主紧缩已经被完成。当然,即使有比hbase.hstore.compaction.max设置的更多的Storefile,用户请求的主紧缩仍会发生。

6. 如果包含少于hbase.hstore.compaction.min数量的StoreFiles,minor紧缩将被中止。注意主紧缩可在单独的HFile上执行。其功能是移除过期版本和删除的数据,并重设StoreFile的本地化。

7. hbase.hstore.compaction.ratio参数的值乘以StoreFiles之和小于某个文件,是用来判断在minor紧缩阶段,StoreFile是否被选为紧缩,例如,如果hbase.hstore.compaction.ratio是1.2, FileX是5MB,FileY是2MB,而FileZ是3MB:

  1. 5 <= 1.2 x (2 + 3) or 5 <= 6

在这个场景中,FileX符合minor紧缩。如果FileX是7MB,它将不符合minor紧缩。这个ratio支持更小的StoreFiles.可以在非繁忙时间配置一个不同的比例,使用参数hbase.hstore.compaction.ratio.offpeak,如果你同时配置了hbase.offpeak.start.hour and hbase.offpeak.end.hour.

8. 如果上一次主紧缩是很久以前,并且这里有不止一个StoreFile需要进行紧缩,主紧缩将运行起来,即使它本来要进行次要紧缩。默认情况下,两次主紧缩之间最长时间为7天,加减4.8小时,由那些参数随机决定。在0.96之前,主紧缩时间为24小时。See hbase.hregion.majorcompaction in the table below to tune or disable time-based major compactions.

紧缩算法使用的参数

表格包含紧缩的主要配置参数,其中没有列举所有。可在hbase-default.xml中编辑这些参数,来实现调优。For a full list of all configuration parameters available, see config.files

  • hbase.hstore.compaction.min

    • 进行紧缩之前符合紧缩的StoreFile的最少数量。调整这个参数的目的是避免太多小的storeFile来紧缩。设置这个值为2将导致每次Store中有两个StoreFiles时,进行次紧缩,这可能并不合适。如果这个值设置的太高,所有其他的参数需要适当进行调整。对大多数情况,默认值已经足够。之前版本中这个参数被叫做hbase.hstore.compactionThreshold
    • 默认3
  • hbase.hstore.compaction.max

    • 被选为单个次紧缩的StoreFile的最大值,不管符合条件的storeFile数量。hbase.hstore.compaction.max 的值控制单个次紧缩完成的时间。设置越大,表名要在紧缩中包含更多的StoreFile.大多数情况下默认值已经足够。
    • 默认为10.
  • hbase.hstore.compaction.min.size

    • 小于这个值的StoreFile符合次紧缩要求,等于或大于这个尺寸的StoreFile根据hbase.hstore.compaction.ratio来确定其是否有资格。因为这个限制是自动包括所有小于这个限制的storeFile的,这个值在写负载多的时候应该减少,在那种情况许多1-2M范围的数据 被刷新,因为每个StoreFile文件将成为紧缩的对象,结果是StoreFile依然小于最小尺寸,并需要进一步紧缩。如果这个参数被降低,比例检查将被更快触发。这个解决早期版本中的某些问题,但在大多数场合中改变这个值不再是必要的。
    • 默认为128M
  • hbase.hstore.compaction.max.size

    • 大于这个值的StoreFile将被排除在紧缩之外。增加这个值的效果很少,大的StoreFile不经常紧缩。如果你觉得紧缩发生的太过频繁而没有太多好处,你可以试着提高这个值。
    • 默认:Long.Max_value
  • hbase.hstore.compaction.ratio
    对次紧缩,这个比率被用来判断某个大于hbase.hstore.compaction.min.size的StoreFile是否符合紧缩。它的作用是限制大文件的紧缩。这个值由浮点小数表示。

    • 大比率,如10,出现大到StoreFile.相反的,如25将引发一种类似大表紧缩的算法,产生4个StoreFile。
    • 建议值为1.0到1.4,当调优这个值时,你是在平衡读写消耗,提高这个值将导致更多的写消耗,因为你将压缩更大的StoreFile,当然,在读数据时,HBase将需要seek更少的StoreFile来完成读文件。
    • 另外,你可以降低这个值,如1,来减少后台写入的成本, and use to limit the number of StoreFiles touched during reads.
    • 默认值为1.2F
  • hbase.hstore.compaction.ratio.offpeak
    如果离峰时间也被配置过,该参数为离峰压缩时使用的压缩比率,使用浮点型小数。

    • 默认为5.0F
  • hbase.offpeak.start.hour
    The start of off-peak hours, expressed as an integer between 0 and 23, inclusive. Set to -1 to disable off-peak.

    • 默认为-1
  • hbase.offpeak.end.hour
    The end of off-peak hours, expressed as an integer between 0 and 23, inclusive. Set to -1 to disable off-peak.

    • 默认为-1

Compaction File Selection

要理解StoreFile选择的核心算法,有一些ASCII表在Store源代码中作为有用的参考。

  1. /* normal skew:
  2. *
  3. * older ----> newer
  4. * _
  5. * | | _
  6. * | | | | _
  7. * --|-|- |-|- |-|---_-------_------- minCompactSize
  8. * | | | | | | | | _ | |
  9. * | | | | | | | | | | | |
  10. * | | | | | | | | | | | |
  11. */

Important knobs:

  • hbase.hstore.compaction.ratio 在压缩文件选择算法中使用的Ratio(默认为1.2f)
  • hbase.hstore.compaction.min(在Hbase v0.9中称为hbase.hstore.compactionThreshold)压缩发生时每个Store中最小的StoreFiles数量(默认为2)
  • hbase.hstore.compaction.max(文件)次要紧缩时StoreFile的最大数量(默认为10)
  • hbase.hstore.compaction.min.size (bytes),小于这个设置的任意StoreFile将成为紧缩的候选对象。默认hbase.hregion.memstore.flush.size(128M)
  • hbase.hstore.compaction.max.size (.92) (bytes) 大于这个值的StoreFile将被排除在紧缩之外。(默认Long.Max_value)

The minor compaction StoreFile selection logic is size based, and selects a file for compaction when the file ⇐ sum(smaller_files) * hbase.hstore.compaction.ratio.

Minor Compaction File Selection - Example #1 (Basic Example)

This example mirrors an example from the unit test TestCompactSelection.

  • hbase.hstore.compaction.ratio = 1.0f
  • hbase.hstore.compaction.min = 3 (files)
  • hbase.hstore.compaction.max = 5 (files)
  • hbase.hstore.compaction.min.size = 10 (bytes)
  • hbase.hstore.compaction.max.size = 1000 (bytes)

The following StoreFiles exist: 100, 50, 23, 12, and 12 bytes apiece.根据上面的参数,应选择23 12和12.为什么?

  • 100 → No, because sum(50, 23, 12, 12) * 1.0 = 97.
  • 50 → No, because sum(23, 12, 12) * 1.0 = 47.
  • 23 → Yes, because sum(12, 12) * 1.0 = 24.
  • 12 → Yes, because the previous file has been included, and because this does not exceed the max-file limit of 5
  • 12 → Yes, because the previous file had been included, and because this does not exceed the max-file limit of 5.

Minor Compaction File Selection - Example #2 (Not Enough Files ToCompact)

This example mirrors an example from the unit test TestCompactSelection.

  • hbase.hstore.compaction.ratio = 1.0f
  • hbase.hstore.compaction.min = 3 (files)
  • hbase.hstore.compaction.max = 5 (files)
  • hbase.hstore.compaction.min.size = 10 (bytes)
  • hbase.hstore.compaction.max.size = 1000 (bytes)

The following StoreFiles exist: 100, 25, 12, and 12 bytes apiece (oldest to newest). With the above parameters, no compaction will be started.

why?

  • 100 → No, because sum(25, 12, 12) * 1.0 = 47
  • 25 → No, because sum(12, 12) * 1.0 = 24
  • 12 → No. Candidate because sum(12) * 1.0 = 12, there are only 2 files to compact and that is less than the threshold of 3
  • 12 → No. Candidate because the previous StoreFile was, but there are not enough files to compact

Minor Compaction File Selection - Example #3 (Limiting Files To Compact)

This example mirrors an example from the unit test TestCompactSelection.

  • hbase.hstore.compaction.ratio = 1.0f
  • hbase.hstore.compaction.min = 3 (files)
  • hbase.hstore.compaction.max = 5 (files)
  • hbase.hstore.compaction.min.size = 10 (bytes)
  • hbase.hstore.compaction.max.size = 1000 (bytes)

The following StoreFiles exist: 7, 6, 5, 4, 3, 2, and 1 bytes apiece (oldest to newest). With the above parameters, the files that would be selected for minor compaction are 7, 6, 5, 4, 3.

why?

  • 7 → Yes, because sum(6, 5, 4, 3, 2, 1) * 1.0 = 21. Also, 7 is less than the min-size
  • 6 → Yes, because sum(5, 4, 3, 2, 1) * 1.0 = 15. Also, 6 is less than the min-size.
  • 5 → Yes, because sum(4, 3, 2, 1) * 1.0 = 10. Also, 5 is less than the min-size.
  • 4 → Yes, because sum(3, 2, 1) * 1.0 = 6. Also, 4 is less than the min-size.
  • 3 → Yes, because sum(2, 1) * 1.0 = 3. Also, 3 is less than the min-size.
  • 2 → No. Candidate because previous file was selected and 2 is less than the min-size, but the max-number of files to compact has been reached.
  • 1 → No. Candidate because previous file was selected and 1 is less than the min-size, but max-number of files to compact has been reached.

日期分层压实 日期分层压实是一个日期感知的存储文件的压缩策略,有利于时间范围扫描的时间序列数据。

不要将其用于:

  • 没有时间范围随机获取
  • 频繁删除和更新
  • 创建长尾巴时频繁无序的数据写入,特别是写未来的时间戳
  • 重叠时间范围内的频繁批量负载

性能改进

性能测试显示在有限时间范围内的时间范围扫描性能大大提高,特别是对最近数据的查询。

启用日期分层压实

通过设置hbase.hstore.engine.class为org.apache.hadoop.hbase.regionserver.DateTieredStoreEngine可以对某个表或列族启用日期分层压实。

也需要设置hbase.hstore.blockingStoreFiles为一个较大值,例如60,if using all default settings, rather than the default value of 12. Use 1.5~2 x projected file count if changing the parameters, Projected file count = windows per tier x tier count + incoming window min + files older than max age

同时,需要设置hbase.hstore.compaction.max和hbase.hstore.blockingStoreFiles为相同的值,来关闭主紧缩。

Procedure: Enable Date Tiered Compaction

1. HBase shell中运行下面的命令。用你的表名替换orders_table。

  1. alter 'orders_table', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.DateTieredStoreEngine', 'hbase.hstore.blockingStoreFiles' => '60', 'hbase.hstore.compaction.min'=>'2', 'hbase.hstore.compaction.max'=>'60'}
  2. alter 'orders_table', {NAME => 'blobs_cf', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.DateTieredStoreEngine', 'hbase.hstore.blockingStoreFiles' => '60', 'hbase.hstore.compaction.min'=>'2', 'hbase.hstore.compaction.max'=>'60'}}
  3. create 'orders_table', 'blobs_cf', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.DateTieredStoreEngine', 'hbase.hstore.blockingStoreFiles' => '60', 'hbase.hstore.compaction.min'=>'2', 'hbase.hstore.compaction.max'=>'60'}

2. Configure other options if needed. See Configuring Date Tiered Compaction for more information

Procedure: Disable Date Tiered Compaction

设置hbase.hstore.engine.class为0或者org.apache.hadoop.hbase.regionserver.DefaultStoreEngine,这两个值有同样的作用。确保将你设置的其他选项为初始设置。

  1. alter 'orders_table', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.DefaultStoreEngine' 'hbase.hstore.blockingStoreFiles' => '12', 'hbase.hstore.compaction.min'=>'6', 'hbase.hstore.compaction.max'=>'12'}}

当改变store engine时,主紧缩将在大多数区域中被执行。这在新表上并不是必要的。

Configuring Date Tiered Compaction 每个日期分层的设置应该在表或列族级别上进行配置。如果使用HBase shell,通常命令如下:

  1. alter 'orders_table', CONFIGURATION => {'key' => 'value', ..., 'key' => 'value'}}

启动条纹紧缩

可以为一个表或列族启动条纹紧缩,通过设置hbase.hstore.engine.class为org.apache.hadoop.hbase.regionserver.StripeStoreEngine,同时设置hbase.hstore.blockingStoreFiles为一个高一点的值,如100,而不是默认值10

Procedure: Enable Stripe Compaction

1. 在Hbase shell中运行下面的命令,用你的表名替换orders_table。

  1. alter 'orders_table', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.StripeStoreEngine', 'hbase.hstore.blockingStoreFiles' => '100'}
  2. alter 'orders_table', {NAME => 'blobs_cf', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.StripeStoreEngine', 'hbase.hstore.blockingStoreFiles' => '100'}}
  3. create 'orders_table', 'blobs_cf', CONFIGURATION => {'hbase.hstore.engine.class' => 'org.apache.hadoop.hbase.regionserver.StripeStoreEngine', 'hbase.hstore.blockingStoreFiles' => '100'}

2. Configure other options if needed. See Configuring Stripe Compaction for more information.

3. Enable the table.

Procedure: Disable Stripe Compaction

1. 设置hbase.hstore.engine.class为0或org.apache.hadoop.hbase.regionserver.DefaultStoreEngine,二者效果相同。

  1. alter 'orders_table', CONFIGURATION => {'hbase.hstore.engine.class' => 'rg.apache.hadoop.hbase.regionserver.DefaultStoreEngine'}

2. Enable the table.

When you enable a large table after changing the store engine either way, a major compaction will likely be performed on most regions. This is not necessary on new tables.

71. 批量加载

71.1. 综述

HBase 包含几种加载数据到表格中的方法。最直接的方法是从MapReduce里使用TableOutputFormat类或使用通常的client APIs,当然这些并不总是最佳方法。批量加载功能使用mapreduce将表格数据以HBase中内部数据格式输出,直接加载产生的StoreFile到运行的集群中。使用批量加载比普通api耗费更少的CPU和网络资源。

71.2. 批量加载限制

由于批量加载会绕过写路径,所以WAL不会将写作为进程的一部分。 Replication works by reading the WAL files so it won’t see the bulk loaded data – and the same goes for the edits that use Put.setDurability(SKIP_WAL)。 One way to handle that is to ship the raw files or the HFiles to the other cluster and do the other processing there.

71.3 批量加载结构

HBase批量加载分为两步。

71.3.1 通过MR 准备数据

批量加载第一步是通过MR使用HFileOutputFormat2生成HBase数据文件(StoreFile)。这个输出格式输出数据采用了HBase的内部存储格式的,以便他们以后可以非常高效的加载到集群中。为了功能有效,HFileOutputFormat2 必须配置成每个输出Hfile适合单个区域。为了做这个,生成批量加载数据的job使用HBase的TotalOrderPartitioner类来将map输出分成键值不相交的区域,与表中区域的键范围一致。

HFileOutputFormat2包含方便的方法,configureIncrementalLoad(),可以在表格区域边界的基础上设置TotalOrderPartitioner。

71.3.2 完成数据加载

在加载数据准备好后,或者使用importtsv工具(importtsv.bulk.output选项)或使用HFileOutputFormat的某些其他的MR job,completebulkload工具被用来将数据导入到运行中的集群中。这个命令行工具遍历准备好的数据文件,为每一个判断文件属于的区域。然后联系适当的区域服务器来手机HFile, 移动它到存储目录,并把数据提供给客户。

如果区域边界在准备批量数据时或在准备和完成之间发生变化,completebulkload 工具将自动分割数据文件,使其与新的边界一致。这个过程可能并不是十分高效,所以用户应该照看好准备加载和导入集群之间的最小延迟,特别是如果有其他客户端通过其他方式同时加载数据。

  1. $ hadoop jar hbase-server-VERSION.jar completebulkload [-c /path/to/hbase/config/hbase-site.xml] /user/todd/myoutput mytable

72.HDFS

HBase在HDFS上运行(每个StoreFile在HDFS以一个文件存在),所以理解HDFS结构特别是它是怎样存储文件、处理异常、复制块,是十分重要的

72.1 NameNode

NameNode 负责维护文件系统数据。

72.2. DataNode

DataNode负责存储HDFS块。

73. 时间线一致的高可用读取

73.1 介绍

在结构上,Hbase总是从一开始具有强一致性保障。所有读写都经过一个单一的区域服务器进行路由,这将保证所有写操作以某个顺序发生,所有读可以获得最近提交的数据。

当然,由于这种单次定位的读取单个位置,如果某个服务器不可用,托管在该区域服务器上的表区域将在一段时间内不可用。恢复分为三个阶段-检测、分配和恢复。在这之中,检测通常是最长的,目前为20-30秒,依据Zookeeper会话超时时间。在这段以及恢复完成之前,客户端将不能读取区域数据。

当然,在某些情况下,或者数据处于只读状态或读取陈旧数据是可以接收的。对于时间线一致高可用读中,HBase可以被用来应付这些延迟敏感的用例,这些应用可以设一个读完成的时间界限。

为了读的高可用性,Hbase提供了region replication的特性。在这个特性中,对每个表区域,将有多个表副本在不同区域服务器中被打开。默认情况下,区域服务器副本数设置为1,所以仅有一个区域副本被部署,且对于原模型不会有任何变化。如果区域副本数设为2或更多,那么master将分发表区域副本。负载均衡保证区域副本不会同时在同一区域服务器中以及同一机架。

每个区域服务器的所有副本将有一个唯一的replica_id, 从0开始。replica_id == 0被叫做主区域,其他的叫第二区域或secondaries.只有主区域接收客户端的写请求,其将总是包含最新的改变。因为所有的写操作需要通过主区域,所以写并非高可用的(意味着可能某时区域不可用写可能被阻塞)

73.2 时间线一致性

有了这个特性,Hbase引入了一致性的定义,提供给每个操作(get或scan)

  1. public enum Consistency {
  2. STRONG,
  3. TIMELINE
  4. }

Consistency.STRONG是Hbase提供的默认一致性模型。在表具有一个区域副本的情况下,或具有区域副本但读通过一致性完成, 读总是由主区域执行,因此,不会有任何更改从以前的行为,而客户端总是观察到最新的数据。

在带Consistency.TIMELINE读的例子中,读的RPC将先被送到首要区域服务器。在短暂的中断后,(hbase.client.primaryCallTimeout.get, 10ms by default),并行的RPC将被发第二区域副本,如果主区域没有返回响应。在这之后,结果从第一个完成的RPC返回。如果响应来自第一主区域,可知道数据总是最新的。对于这一结果,isState()接口被用来检查是否过期。如果结果来自第二区域,那么结果isState将被设为true。之后,用户可以来观察数据可能的原因。

从语义方面,Hbase实现时间线一致性不同于纯正的一致性,有以下方面的区别:

  • 单宿主和有序更新:区域复制与否, 在写端,只有一个定义的副本(主)可以接受写入。副本负责记录edits顺序,并防止冲突。这保证两个不同的写操作不会在同时被不同的副本和数据diverges提交,有了这个不需要做读read-repair和last-timestamp-wins冲突的解决方法。
  • secondaries也按照primary提交的顺序应用编辑。这样secondaries将尽快包含主数据的在任意点的快照。这与RDBMS复制类似,即使Hbase自己的多数据中心复制,虽然在一个集群中。
  • 在读一端,客户端可以检测读数据是否来自最新的数据或是过期数据。同样,客户端可以在每个操作基础上发出不同一致性要求的读取,来确保其自身语义。
  • 客户端也可以发现不按序的编辑文件,如果发现某个读先来自第二副本能及时返回,区域副本或基于事务ID的担保没有粘性。如果需要,后期可以实现这个。

Timeline Consistency

要好好理解时间线一致性,可以看看上面表格。假定有两个客户端,第一个在开始时写x =1,然后x = 2, x = 3。向上面那样,所有的写操作由主区域处理。写操作被存储到WAL中, 异步复制到其他副本上。在上面的图表中,注意replica_id=1收到2个更新, 数据显示x = 2,而replica_id=2只收到一个更新,数据显示x = 1。

如果客户端1以STRONG consistency方式读取数据,其只与replica_id=0进行通话,因此可以保证发现最新的数据x=3.在客户端发起TIMELINE consistency一致性读取时,RPC将前往所有replicas(在primary timeout之后),第一给响应的结果将被返回。因此客户端可能看到1,2,3任意值。我们称主区域失败,一段时间内日志复制不能进行。如果客户端以TIMELINE consistency进行多次读取,她可能先获得x=2,然后x=1等等。

73.3. 权衡取舍

有次要区域管理读取可用性提出了某些事物需要仔细权衡,根据每个例子认真评估。下面是优缺点:

优点:

  • 只读表的高可用性
  • 陈旧读取的高可用性
  • Ability to do very low latency reads with very high percentile (99.9%+) latencies for stale reads

缺点:

  • 对表格区域复制大于1的情况:双倍或三倍内存使用(根据区域副本数量)
  • 增加块缓存使用
  • 用于日志复制的额外网络流量
  • 额外备份RPC

要提供区域数据的多副本,HBase在区域服务器中以第二模式打开区域。打开第二模式的区域将与主区域副本共享同一数据文件,当然每个第二区域副本将有其自己的内存来保存尚未刷写的数据(只有主区域可以进行flush)同样,要提供第二区域的读取,数据文件块可能也要缓存在块缓存中。

73.4. Where is the code

This feature is delivered in two phases, Phase 1 and 2. The first phase is done in time for HBase-1.0.0 release. Meaning that using HBase-1.0.x, you can use all the features that are marked for Phase 1. Phase 2 is committed in HBase-1.1.0, meaning all HBase versions after 1.1.0 should contain Phase 2 items.

73.5 Propagating writes to region replicas

向上面讨论的那样,读只在主区域上进行。从主区域复制写入第二区域中,这里有两种不同机制。对只读表,不需要使用下面任意方法, Disabling and enabling the table 应该使数据对所有区域副本可用。对可变表,只能使用下面一种机制:storefile refresher, or async wal replication. 建议后者。

73.5.1 StoreFile Refresher

第一种机制是 StoreFile Refresher,其在HBase1.0+引入。 StoreFile Refresher是每个区域一个线程,周期性为第二区域副本的主区域Store file执行刷新操作。如果启用,将确保第二区域副本从主区域以及时的方式see新的被刷新的、紧缩的、批量加载的文件。当然,这意味着只有刷新的数据可以被读回第二区域副本,在refresher运行之后,使第二区域长时间落后主。

For turning this feature on, you should configure hbase.regionserver.storefile.refresh.period to a non-zero value. See Configuration section below.

73.5.2 Asnyc WAL replication

第二种实现复制写入secondaries的机制是通过“Asnyc WAL replication”特性实现,只在1.1之后才有。这个工作原理与Hbase多数据中心复制,但是将数据复制到第二区域。每个第二区域副本总能按主区域提交给他们的顺序接收并观察写入操作。在某种意义上,这种设计可被认为集群内复制,代替复制到不同的数据中心,数据到达第二区域使第二区域中的内存状态保持最新。数据文件在主区域和其他副本之间共享,所以没有额外的存储开销。当然,第二区域将具有最近尚未刷新的数据在内存中, 这将增加内存开销。主区域写入刷新、紧缩、以及批量加载事件到它的WAL中,这些也将通过wal复制到第二区域中。当它们观察flush、compaction或批量加载事件时,第二区域回放事件来获取新文件并删除旧文件。

按primary顺序提交写操作确保secondaries不会偏离主区域数据,但由于log复制是异步的,数据在secondary区域中仍是稳定的。由于这个特性作为复制的终点,性能和延迟特性和集群内复制相似。

异步WAL复制默认关闭,可以设置hbase.region.replica.replication.enabled为true来启用这个功能。异步WAL复制功能将增加一个新的复制peer,称为region_replica_replication作为首次创建区域副本大于1的表格的复制peer。Once enabled, if you want to disable this feature, you need to do two actions: Set configuration property hbase.region.replica.replication.enabled to false in hbase-site.xml (see Configuration section below) Disable the replication peer named region_replica_replication in the cluster using hbase shell or ReplicationAdmin class:

  1. hbase> disable_peer 'region_replica_replication'

73.6. Store File TTL

在上面提到的write propagation approaches中,主存储文件将在每个次区域中打开,这是独立于主区域的。所以, 次区域可能依然在参考那些被主区域压缩的次区域进行读取。这些功能都使用HFilesLinks到参考文件,但目前(尚未)还没有保护来防止文件被过早的删除。因此,作为一种防护措施,要设置配置属性hbase.master.hfilecleaner.ttl为一个较大的值,例如为1h来保证不会在请求副本时收到IOException

73.7. META表区域的区域复制

当前,异步WAL复制不是为META表WAL而做,meta表的次副本仍从持久化的persistent store file中获取刷新。因此hbase.regionserver.meta.storefile.refresh.period需要设置成某个非零值来刷新meta store file.注意这个配置和hbase.regionserver.storefile.refresh.period配置不同。

73.8. 内存accounting

次区域副本参考主区域副本的数据文件,但它们具有自己的memstore (Hbase 1.1之后),并使用block cache。当然,一个不同是第二区域副本不能刷新数据,当他们memstores有内存压力时。当主区域进行刷新且这个刷新被复制到次区域中,它们只能释放memstore内存。因为在一个区域服务器管理主副本的一些其他区域,次级可能导致到主区域同一host的额外刷新。在极端的情况下,可能没有给来自主区域WAL复制的新的writes留下足够的内存空间。为了疏导这种情况(且因为次区域不能自己进行刷新),次区域允许执行一个“store file refresh”的文件系统list操作来从主区域获取新文件,并可能自己删除memstore.该刷新操作只会在最大的次区域副本memstore size至少是主区域最大副本memstore 的hbase.region.replica.storefile.refresh.memstore.multiplier倍(默认为4)。需要注意的是,如果这个被执行了,第二区域能观察局部行在列族上的更新(因为列族flush是独立进行的)。默认情况下不会经常做这个操作。可以将这个值设置的较大来关闭这个功能,但要小心,这可能导致复制被永久中断。

73.9. 次副本失败

当第二区域副本第一次上线,其可能要执行某些来自memstore的修改。因为在次副本上的恢复是不同的,次副本需要保证在请求分配之后其响应该请求之前,不会倒退。为了实现这点,次级会一直等待,直到发现一个完整的刷新周期(开始flush,提交flush)或发现来自主区域的某个“区域打开事件”。直到这个发生,次区域副本将拒绝所有读请求并抛出IOException(The region’s reads are disabled)。当然,其他副本可能依然可读,因此不会导致任何rpc TIMELINE 一致性的影响。要实现更快的恢复,次区域当其打开时,会触发来自主区域的flush请求hbase.region.replica.wait.for.primary.flush属性来关闭这个功能。

73.10. 配置属性

要使用高可得性读取操作,应该设置hbase-site.xml中下面的属性。这里没有特定的属性来启用或关闭区域复制。可以改变每个表的区域副本数,在表的创建或修改时。The following configuration is for using async wal replication and using meta replicas of 3.

73.10.1 服务端配置

  1. <property>
  2. <name>hbase.regionserver.storefile.refresh.period</name>
  3. <value>0</value>
  4. <description>
  5. The period (in milliseconds) for refreshing the store files for the secondary regions. 0 means this feature is disabled. Secondary regions sees new files (from flushes and compactions) from primary once the secondary region refreshes the list of files in the region (there is no notification mechanism). But too frequent refreshes might cause extra Namenode pressure. If the files cannot be refreshed for longer than HFile TTL (hbase.master.hfilecleaner.ttl) the requests are rejected. Configuring HFile TTL to a larger value is also recommended with this setting.
  6. </description>
  7. </property>
  8. <property>
  9. <name>hbase.regionserver.meta.storefile.refresh.period</name>
  10. <value>300000</value>
  11. <description>
  12. The period (in milliseconds) for refreshing the store files for the hbase:meta tables secondary regions. 0 means this feature is disabled. Secondary regions sees new files (from flushes and compactions) from primary once the secondary region refreshes the list of files in the region (there is no notification mechanism). But too frequent refreshes might cause extra Namenode pressure. If the files cannot be refreshed for longer than HFile TTL (hbase.master.hfilecleaner.ttl) the requests are rejected. Configuring HFile TTL to a larger value is also recommended with this setting. This should be a non-zero number if meta replicas are enabled (via hbase.meta.replica.count set to greater than 1).
  13. </description>
  14. </property>
  15. <property>
  16. <name>hbase.region.replica.replication.enabled</name>
  17. <value>true</value>
  18. <description>
  19. Whether asynchronous WAL replication to the secondary region replicas is enabled or not. If this is enabled, a replication peer named "region_replica_replication" will be created which will tail the logs and replicate the mutations to region replicas for tables that have region replication > 1. If this is enabled once, disabling this replication also requires disabling the replication peer using shell or ReplicationAdmin java class. Replication to secondary region replicas works over standard inter-cluster replication.
  20. </description>
  21. </property>
  22. <property>
  23. <name>hbase.region.replica.replication.memstore.enabled</name>
  24. <value>true</value>
  25. <description>
  26. If you set this to `false`, replicas do not receive memstore updates from the primary RegionServer. If you set this to `true`, you can still disable memstore replication on a per-table basis, by setting the table's `REGION_MEMSTORE_REPLICATION` configuration property to `false`. If memstore replication is disabled, the secondaries will only receive updates for events like flushes and bulkloads, and will not have access to ata which the primary has not yet flushed. This preserves the guarantee of row-level consistency, even when the read requests `Consistency.TIMELINE`.
  27. </description>
  28. </property>
  29. <property>
  30. <name>hbase.master.hfilecleaner.ttl</name>
  31. <value>3600000</value>
  32. <description>
  33. The period (in milliseconds) to keep store files in the archive folder before deleting them from the file system.</description>
  34. </property>
  35. <property>
  36. <name>hbase.meta.replica.count</name>
  37. <value>3</value>
  38. <description>
  39. Region replication count for the meta regions. Defaults to 1.
  40. </description>
  41. </property>
  42. <property>
  43. <name>hbase.region.replica.storefile.refresh.memstore.multiplier</name>
  44. <value>4</value>
  45. <description>
  46. The multiplier for a “store file refresh” operation for the secondary region replica. If a region server has memory pressure, the secondary region will refresh it’s store files if the memstore size of the biggest secondary replica is bigger this many times than the memstore size of the biggest primary replica. Set this to a very big value to disable this feature (not recommended).
  47. </description>
  48. </property>
  49. <property>
  50. <name>hbase.region.replica.wait.for.primary.flush</name>
  51. <value>true</value>
  52. <description>
  53. Whether to wait for observing a full flush cycle from the primary before start serving data in a secondary. Disabling this might cause the secondary region replicas to go back in time for reads between region movements.
  54. </description>
  55. </property>

73.10.2 客户端配置

确保为所有将使用区域副本的客户机(和服务器)设置以下内容:

  1. <property>
  2. <name>hbase.ipc.client.specificThreadForWriting</name>
  3. <value>true</value>
  4. <description>
  5. Whether to enable interruption of RPC threads at the client side. This is required for region replicas with fallback RPC’s to secondary regions.
  6. </description>
  7. </property>
  8. <property>
  9. <name>hbase.client.primaryCallTimeout.get</name>
  10. <value>10000</value>
  11. <description>
  12. The timeout (in microseconds), before secondary fallback RPC’s are submitted for get requests with Consistency.TIMELINE to the secondary replicas of the regions. Defaults to 10ms. Setting this lower will increase the number of RPC’s, but will lower the p99 latencies.
  13. </description>
  14. </property>
  15. <property>
  16. <name>hbase.client.primaryCallTimeout.multiget</name>
  17. <value>10000</value>
  18. <description>
  19. The timeout (in microseconds), before secondary fallback RPC’s are submitted for multi-get requests (Table.get(List<Get>)) with Consistency.TIMELINE to the secondary replicas of the regions. Defaults to 10ms. Setting this lower will increase the number of RPC’s, but will lower the p99 latencies.
  20. </description>
  21. </property>
  22. <property>
  23. <name>hbase.client.replicaCallTimeout.scan</name>
  24. <value>1000000</value>
  25. <description>
  26. The timeout (in microseconds), before secondary fallback RPC’s are submitted for scan requests with Consistency.TIMELINE to the secondary replicas of the regions. Defaults to 1 sec. Setting this lower will increase the number of RPC’s, but will lower the p99 latencies.
  27. </description>
  28. </property>
  29. <property>
  30. <name>hbase.meta.replicas.use</name>
  31. <value>true</value>
  32. <description>
  33. Whether to use meta table replicas or not. Default is false.
  34. </description>
  35. </property>

注意HBase1.0.x要使用hbase.ipc.client.allowsInterrupt而不是hbase.ipc.client.specificThreadForWriting

73.11. 用户界面

在主用户界面,表格区域副本和主区域一起显示。可以注意到区域的副本将共享相同的起始和结束键以及同样的区域名前缀。唯一的区别就是附加replica_id(以HEX编码)。区域的编码名字将不同。可在UI中显式的显示副本ID。

73.12. 使用区域复制创建表格

区域复制是表格的属性。所有表格默认REGION_REPLICATION = 1,代表每个区域只有一个副本。可以设置或改变一张表格每个区域的副本数量,通过在表描述符中提供region_replication属性。

73.12.1. shell

  1. create 't1', 'f1', {REGION_REPLICATION => 2}
  2. describe 't1'
  3. for i in 1..100
  4. put 't1', "r#{i}", 'f1:c1', i
  5. end
  6. flush 't1'

73.12.2. Java

  1. HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(“test_table”));
  2. htd.setRegionReplication(2);
  3. ...
  4. admin.createTable(htd);

也可以使用setRegionReplication()来调整表格增加或减少其区域副本。

73.13. Read API and Usage

73.13.1. Shell

可以在shell中使用以下 Consistency.TIMELINE语句来读取:

  1. hbase(main):001:0> get 't1','r6', {CONSISTENCY => "TIMELINE"}

可以模拟一个区域服务器的暂停或不可用并从次区域获取数据:

  1. $ kill -STOP <pid or primary region server>
  2. hbase(main):001:0> get 't1','r6', {CONSISTENCY => "TIMELINE"}

Using scans is also similar

  1. hbase> scan 't1', {CONSISTENCY => 'TIMELINE'}

73.13.2. Java

You can set the consistency for Gets and Scans and do requests as follows.

  1. Get get = new Get(row);
  2. get.setConsistency(Consistency.TIMELINE);
  3. ...
  4. Result result = table.get(get);

You can also pass multiple gets:

  1. Get get1 = new Get(row);
  2. get1.setConsistency(Consistency.TIMELINE);
  3. ...
  4. ArrayList<Get> gets = new ArrayList<Get>();
  5. gets.add(get1);
  6. ...
  7. Result[] results = table.get(gets);

And Scans:

  1. Scan scan = new Scan();
  2. scan.setConsistency(Consistency.TIMELINE);
  3. ...
  4. ResultScanner scanner = table.getScanner(scan);

You can inspect whether the results are coming from primary region or not by calling the Result.isStale() method:

  1. Result result = table.get(get);
  2. if (result.isStale()) {
  3. ...
  4. }

74. 存储中等大小的对象 (MOB)

存储在HBase中数据有许多尺寸,包含二进制数据如图像和文件。而HBase技术上可以处理大小大于100kb的二进制对象。HBase正常的读写路径是经过优化配置的适于大小小于100kb的对象。当HBase处理大于该阈值对象时,即这里指的中等对象(MOB)性能可能退化,这是由于分裂和紧缩导致的写放大引起的。当使用MOBs时,理想目标大小是100KB到10MB之间。HBase FIX_VERSION_NUMBER增加了当维护性能、一致性、以及低运行开销时,管理大量MOB的更好表现。

74.1. Configuring Columns for MOB

在表创建或变化时可以配置列来支持MOB,通过HBase Shell或通过Java API。有两个相关的属性值是boolean 型的IS_MOB和MOB_THRESHOLD,被认为是MOB对象的字节数。只需要IS_MOB。如果不指定MOB_THRESHOLD默认使用100KB为阈值。

Example 37. Configure a Column for MOB Using HBase Shell

  1. hbase> create 't1', {NAME => 'f1', IS_MOB => true, MOB_THRESHOLD => 102400}
  2. hbase> alter 't1', {NAME => 'f1', IS_MOB => true, MOB_THRESHOLD => 102400}

Example 38. Configure a Column for MOB Using the Java API

  1. ...
  2. HColumnDescriptor hcd = new HColumnDescriptor(“f”);
  3. hcd.setMobEnabled(true);
  4. ...
  5. hcd.setMobThreshold(102400L);
  6. ...

74.2 测试MOB

org.apache.hadoop.hbase.IntegrationTestIngestMOB工具被用来辅助测试MOB功能。该工具以下面方法运行:

  1. $ sudo -u hbase hbase org.apache.hadoop.hbase.IntegrationTestIngestMOB \
  2. -threshold 102400 \
  3. -minMobDataSize 512 \
  4. -maxMobDataSize 5120
  • 阈值threshold 即哪个cells被认为是MOBs的阈值,默认是1Kb,以字节表示
  • minMobDataSize 是MOB数据最小值。默认为512B,以字节表示。
  • MaxMobDataSize是MOB数据的最大值。默认为是5Kb,以字节表示。

74.3. 配置MOB缓存

因为在任何时间对比HFiles文件数量,都可能有大量的MOB文件,MOB文件不总是打开状态。MOB文件缓存是一种LRU缓存,记录了最近被使用的MOB文件。要在每个RegionServer上配置读缓存,增加下面属性到区域服务器的hbase-site.xml,根据你的环境自定义相关配置,重启RegionServer

Example 39. Example MOB Cache Configuration

  1. <property>
  2. <name>hbase.mob.file.cache.size</name>
  3. <value>1000</value>
  4. <description>
  5. Number of opened file handlers to cache.
  6. A larger value will benefit reads by providing more file handlers per mob
  7. file cache and would reduce frequent file opening and closing.
  8. However, if this is set too high, this could lead to a "too many opened file handers"
  9. The default value is 1000.
  10. </description>
  11. </property>
  12. <property>
  13. <name>hbase.mob.cache.evict.period</name>
  14. <value>3600</value>
  15. <description>
  16. The amount of time in seconds after which an unused file is evicted from the
  17. MOB cache. The default value is 3600 seconds.
  18. </description>
  19. </property>
  20. <property>
  21. <name>hbase.mob.cache.evict.remain.ratio</name>
  22. <value>0.5f</value>
  23. <description>
  24. A multiplier (between 0.0 and 1.0), which determines how many files remain cached
  25. after the threshold of files that remains cached after a cache eviction occurs
  26. which is triggered by reaching the `hbase.mob.file.cache.size` threshold.
  27. The default value is 0.5f, which means that half the files (the least-recently-used
  28. ones) are evicted.
  29. </description>
  30. </property>

74.4. MOB 优化任务

74.4.1.手动压缩MOB文件

手动紧缩而不是等待配置来触发紧缩,使用HBase shell中compact_mob或major_compact_mob。命令要求第一个参数为表名,第二个变量为可选的列族名。如果省略了列族,所有启用MOB的列族都将被紧缩。

  1. hbase> compact_mob 't1', 'c1'
  2. hbase> compact_mob 't1'
  3. hbase> major_compact_mob 't1', 'c1'
  4. hbase> major_compact_mob 't1'

74.4.2 MOB 清扫车

HBase MOB的 a MapReduce job 叫the Sweeper tool.清扫工具通过小文件删除或更新来实现MOB合并,清扫工具不要求是否依靠MR的使用本地MOB紧缩。

To configure the Sweeper tool, set the following options:

  1. <property>
  2. <name>hbase.mob.sweep.tool.compaction.ratio</name>
  3. <value>0.5f</value>
  4. <description>
  5. If there are too many cells deleted in a mob file, it's regarded as an invalid file and needs to be merged. If existingCellsSize/mobFileSize is less than ratio, it's regarded as an invalid file. The default value is 0.5f.
  6. </description>
  7. </property>
  8. <property>
  9. <name>hbase.mob.sweep.tool.compaction.mergeable.size</name>
  10. <value>134217728</value>
  11. <description>
  12. If the size of a mob file is less than this value, it's regarded as a small file and needs to be merged. The default value is 128MB.
  13. </description>
  14. </property>
  15. <property>
  16. <name>hbase.mob.sweep.tool.compaction.memstore.flush.size</name>
  17. <value>134217728</value>
  18. <description>
  19. The flush size for the memstore used by sweep job. Each sweep reducer owns such a memstore.
  20. The default value is 128MB.
  21. </description>
  22. </property>
  23. <property>
  24. <name>hbase.master.mob.ttl.cleaner.period</name>
  25. <value>86400</value>
  26. <description>
  27. The period that ExpiredMobFileCleanerChore runs. The unit is second.
  28. The default value is one day.
  29. </description>
  30. </property>

Next, add the HBase install directory, $HBASE_HOME/*, and HBase library directory to yarn-site.xml Adjust this example to suit your environment.

  1. <property>
  2. <description>Classpath for typical applications.</description>
  3. <name>yarn.application.classpath</name>
  4. <value>
  5. $HADOOP_CONF_DIR,
  6. $HADOOP_COMMON_HOME/*,$HADOOP_COMMON_HOME/lib/*,
  7. $HADOOP_HDFS_HOME/*,$HADOOP_HDFS_HOME/lib/*,
  8. $HADOOP_MAPRED_HOME/*,$HADOOP_MAPRED_HOME/lib/*,
  9. $HADOOP_YARN_HOME/*,$HADOOP_YARN_HOME/lib/*,
  10. $HBASE_HOME/*, $HBASE_HOME/lib/*
  11. </value>
  12. </property>

Finally, run the sweeper tool for each column which is configured for MOB.

  1. $ org.apache.hadoop.hbase.mob.compactions.Sweeper _tableName_ _familyName_